uncoded 0.5.0__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uncoded-0.6.0/AGENTS.md +155 -0
- uncoded-0.6.0/CLAUDE.md +155 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/PKG-INFO +1 -1
- uncoded-0.6.0/src/uncoded/instruction_files.py +164 -0
- uncoded-0.5.0/AGENTS.md +0 -124
- uncoded-0.5.0/CLAUDE.md +0 -124
- uncoded-0.5.0/src/uncoded/instruction_files.py +0 -133
- {uncoded-0.5.0 → uncoded-0.6.0}/.agents/skills/coherence-review/SKILL.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.claude/settings.json +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.claude/skills/coherence-review/SKILL.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.github/workflows/ci.yml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.github/workflows/publish.yml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.gitignore +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.mcp.json +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.pre-commit-config.yaml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.serena/project.yml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/namespace.yaml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/reviews/2026-04-25-001215.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/__init__.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/cli.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/config.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/extract.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/instruction_files.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/namespace_map.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/serena_setup.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/skill.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/stubs.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/sync.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_cli.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_config.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_extract.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_instruction_files.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_namespace_map.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_serena_setup.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_skill.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_stubs.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_sync.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_uncoded.pyi +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/LICENSE +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/MAINTENANCE.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/README.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/SYSTEM.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/pyproject.toml +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/resources/lsp-research.md +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/__init__.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/cli.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/config.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/extract.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/namespace_map.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/serena_setup.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/skill.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/stubs.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/sync.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/__init__.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_cli.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_config.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_extract.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_instruction_files.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_namespace_map.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_serena_setup.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_skill.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_stubs.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_sync.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_uncoded.py +0 -0
- {uncoded-0.5.0 → uncoded-0.6.0}/uv.lock +0 -0
uncoded-0.6.0/AGENTS.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# uncoded
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
AI coding agents navigate codebases poorly. They grep for guessed keywords,
|
|
6
|
+
skim the first few lines of files, and fill gaps from pretraining rather than
|
|
7
|
+
reading the actual code. System prompts encourage this with "make reasonable
|
|
8
|
+
assumptions." The result is plausible-looking output built on a hallucinated
|
|
9
|
+
understanding of code that's sitting right there, unread.
|
|
10
|
+
|
|
11
|
+
## Approach
|
|
12
|
+
|
|
13
|
+
A static, pre-computed index gives agents a top-down view of a codebase.
|
|
14
|
+
The agent loads the index at the start of a task, sees the full vocabulary
|
|
15
|
+
of the code, and navigates deterministically to what it needs — no guessing,
|
|
16
|
+
no grep.
|
|
17
|
+
|
|
18
|
+
Two-level index:
|
|
19
|
+
|
|
20
|
+
1. **Namespace map** (`.uncoded/namespace.yaml`) — a hierarchical YAML file
|
|
21
|
+
listing all symbols: directories, files, classes (with attributes
|
|
22
|
+
and methods), and functions. Covers both source and tests. Loaded into
|
|
23
|
+
context before any task begins. Gives the agent a world view.
|
|
24
|
+
|
|
25
|
+
2. **Stub files** (`.uncoded/stubs/`) — one `.pyi` per source file, with
|
|
26
|
+
imports, full signatures (parameter names, types, return types),
|
|
27
|
+
first-sentence docstrings, module constants, and class attributes.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
This project uses [uv](https://docs.astral.sh/uv/). Run commands via
|
|
32
|
+
`uv run` so they execute inside the project environment without needing
|
|
33
|
+
an activated venv.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
# Generate (or update) the namespace map, stub files, and CLAUDE.md section
|
|
37
|
+
uv run uncoded sync
|
|
38
|
+
|
|
39
|
+
# Run tests
|
|
40
|
+
uv run pytest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
<!-- uncoded:start -->
|
|
44
|
+
## How to read and edit code in this codebase
|
|
45
|
+
|
|
46
|
+
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
47
|
+
a symbol index over its source code, with
|
|
48
|
+
[Serena](https://github.com/oraios/serena) providing language-server-backed
|
|
49
|
+
tools over that index. The point of this scaffolding is one rule.
|
|
50
|
+
|
|
51
|
+
### The rule
|
|
52
|
+
|
|
53
|
+
**If your search term is the name of a Python symbol — a class, function,
|
|
54
|
+
method, attribute, or module-level constant — use the index. If it's a
|
|
55
|
+
pattern, regex, or free-text phrase, use grep.**
|
|
56
|
+
|
|
57
|
+
This applies to every tool call where you are trying to find code, not
|
|
58
|
+
just the first one in the session. The pretrained reflex for "find X" is
|
|
59
|
+
grep, and that reflex is wrong here. `grep -rn 'WriteResult'` to locate a
|
|
60
|
+
class is the rule firing — that is `find_symbol`. `grep -rn 'function_name'`
|
|
61
|
+
to check whether something has callers before a refactor is the rule
|
|
62
|
+
firing — that is `find_referencing_symbols`. `grep` then `Edit` to delete
|
|
63
|
+
dead code is the rule firing — that is `safe_delete_symbol`. The grep
|
|
64
|
+
version of any of these is noisier and less complete: grep misses
|
|
65
|
+
re-exports, and adds false positives from comments, strings, and unrelated
|
|
66
|
+
attribute lookups on other types. The language server does not.
|
|
67
|
+
|
|
68
|
+
### How to execute the rule
|
|
69
|
+
|
|
70
|
+
The index has two parts (a namespace map and per-file stubs) and three
|
|
71
|
+
steps (orient, understand, act).
|
|
72
|
+
|
|
73
|
+
**Step 1 — Orient. Read the namespace map first.** Before answering the
|
|
74
|
+
user, before any other tool call:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Read .uncoded/namespace.yaml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This lists every symbol in the codebase — directories, files, classes,
|
|
81
|
+
methods, functions — in source order. Without it loaded, "find X"
|
|
82
|
+
answers come from pretrained guesses about what a project like this
|
|
83
|
+
*probably* contains, rather than what is actually here. Read it once,
|
|
84
|
+
in full, at session start.
|
|
85
|
+
|
|
86
|
+
**Step 2 — Understand. Read the `.pyi` stub before any `.py` source.**
|
|
87
|
+
Stub paths mirror source paths under `.uncoded/stubs/`:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
91
|
+
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every file you intend to touch or reference, including tests. The stub
|
|
95
|
+
contains imports, every signature with types, module-level assignments,
|
|
96
|
+
class attributes, and first-sentence docstrings — enough for most
|
|
97
|
+
navigation. Skipping straight to source means reading many lines to
|
|
98
|
+
learn what the stub would have told you in one. If no stub exists at
|
|
99
|
+
the expected path, the file has no symbols indexed; in that narrow
|
|
100
|
+
case, read source directly.
|
|
101
|
+
|
|
102
|
+
**Step 3 — Act. Use Serena to find, read, rename, edit, and delete symbols.**
|
|
103
|
+
With the map and stub loaded, you have the exact `relative_path` and
|
|
104
|
+
`name_path` each Serena tool needs (`ClassName/method` for a method,
|
|
105
|
+
`function_name` for a top-level function). Per task:
|
|
106
|
+
|
|
107
|
+
- **Find a symbol's definition, or read its body.** `find_symbol` —
|
|
108
|
+
with `include_body=True` when you need implementation detail the
|
|
109
|
+
stub does not show. Returns exactly the symbol; no offset arithmetic,
|
|
110
|
+
no risk of reading too much. Stay on stubs for a wider sweep.
|
|
111
|
+
|
|
112
|
+
- **Find callers, or check whether a symbol is dead.**
|
|
113
|
+
`find_referencing_symbols`. Returns every reference resolved by the
|
|
114
|
+
language server — every import, every call site, every re-export.
|
|
115
|
+
Grep on the name misses re-exports and adds false positives from
|
|
116
|
+
comments, strings, and attribute lookups on other types. If the
|
|
117
|
+
next move depends on the answer being complete, grep cannot give
|
|
118
|
+
you that.
|
|
119
|
+
|
|
120
|
+
- **Rename.** `rename_symbol`. Updates every reference across the
|
|
121
|
+
codebase in one call. Multi-file find-and-replace misses imports
|
|
122
|
+
and re-exports and racks up substring false positives.
|
|
123
|
+
|
|
124
|
+
- **Edit a symbol as a unit.** `replace_symbol_body`,
|
|
125
|
+
`insert_before_symbol`, `insert_after_symbol`. Immune to the Edit
|
|
126
|
+
tool's "string not unique" failure mode, never modify a similarly
|
|
127
|
+
named neighbour, and keep surrounding indentation consistent.
|
|
128
|
+
|
|
129
|
+
- **Delete a symbol.** `safe_delete_symbol` — not Edit, and not Edit
|
|
130
|
+
after a manual reference check. The tool fuses two operations: it
|
|
131
|
+
finds every reference, refuses to delete if any are live, and
|
|
132
|
+
removes the symbol cleanly only when it is truly dead. The two-step
|
|
133
|
+
version (find references with grep or `find_referencing_symbols`,
|
|
134
|
+
then Edit) can drift — the reference check goes stale the moment
|
|
135
|
+
any file changes between the calls. Whenever the task is "remove
|
|
136
|
+
this symbol," regardless of how dead it looks, this is the tool.
|
|
137
|
+
|
|
138
|
+
### Where Read, Edit, and grep are still the right tools
|
|
139
|
+
|
|
140
|
+
The rule is about source navigation by symbol name. Outside that, the
|
|
141
|
+
non-Serena tools stay correct:
|
|
142
|
+
|
|
143
|
+
- Free-text or pattern search outside source: Markdown, YAML, TOML,
|
|
144
|
+
configs, commit messages, notebook JSON, fixture data.
|
|
145
|
+
- Pattern search across signatures (regex over type annotations,
|
|
146
|
+
decorator usage, alias declarations) — these are not symbol-name
|
|
147
|
+
lookups even though they sit inside source.
|
|
148
|
+
- Partial-line edits inside a symbol body, once you have retrieved it
|
|
149
|
+
through Serena.
|
|
150
|
+
- Environments where Serena is unavailable, or the rare stub-less
|
|
151
|
+
Python file that needs exploratory reading.
|
|
152
|
+
|
|
153
|
+
The dispatch test is the search term: a symbol name → Serena; a regex
|
|
154
|
+
or free-text phrase → grep.
|
|
155
|
+
<!-- uncoded:end -->
|
uncoded-0.6.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# uncoded
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
AI coding agents navigate codebases poorly. They grep for guessed keywords,
|
|
6
|
+
skim the first few lines of files, and fill gaps from pretraining rather than
|
|
7
|
+
reading the actual code. System prompts encourage this with "make reasonable
|
|
8
|
+
assumptions." The result is plausible-looking output built on a hallucinated
|
|
9
|
+
understanding of code that's sitting right there, unread.
|
|
10
|
+
|
|
11
|
+
## Approach
|
|
12
|
+
|
|
13
|
+
A static, pre-computed index gives agents a top-down view of a codebase.
|
|
14
|
+
The agent loads the index at the start of a task, sees the full vocabulary
|
|
15
|
+
of the code, and navigates deterministically to what it needs — no guessing,
|
|
16
|
+
no grep.
|
|
17
|
+
|
|
18
|
+
Two-level index:
|
|
19
|
+
|
|
20
|
+
1. **Namespace map** (`.uncoded/namespace.yaml`) — a hierarchical YAML file
|
|
21
|
+
listing all symbols: directories, files, classes (with attributes
|
|
22
|
+
and methods), and functions. Covers both source and tests. Loaded into
|
|
23
|
+
context before any task begins. Gives the agent a world view.
|
|
24
|
+
|
|
25
|
+
2. **Stub files** (`.uncoded/stubs/`) — one `.pyi` per source file, with
|
|
26
|
+
imports, full signatures (parameter names, types, return types),
|
|
27
|
+
first-sentence docstrings, module constants, and class attributes.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
This project uses [uv](https://docs.astral.sh/uv/). Run commands via
|
|
32
|
+
`uv run` so they execute inside the project environment without needing
|
|
33
|
+
an activated venv.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
# Generate (or update) the namespace map, stub files, and CLAUDE.md section
|
|
37
|
+
uv run uncoded sync
|
|
38
|
+
|
|
39
|
+
# Run tests
|
|
40
|
+
uv run pytest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
<!-- uncoded:start -->
|
|
44
|
+
## How to read and edit code in this codebase
|
|
45
|
+
|
|
46
|
+
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
47
|
+
a symbol index over its source code, with
|
|
48
|
+
[Serena](https://github.com/oraios/serena) providing language-server-backed
|
|
49
|
+
tools over that index. The point of this scaffolding is one rule.
|
|
50
|
+
|
|
51
|
+
### The rule
|
|
52
|
+
|
|
53
|
+
**If your search term is the name of a Python symbol — a class, function,
|
|
54
|
+
method, attribute, or module-level constant — use the index. If it's a
|
|
55
|
+
pattern, regex, or free-text phrase, use grep.**
|
|
56
|
+
|
|
57
|
+
This applies to every tool call where you are trying to find code, not
|
|
58
|
+
just the first one in the session. The pretrained reflex for "find X" is
|
|
59
|
+
grep, and that reflex is wrong here. `grep -rn 'WriteResult'` to locate a
|
|
60
|
+
class is the rule firing — that is `find_symbol`. `grep -rn 'function_name'`
|
|
61
|
+
to check whether something has callers before a refactor is the rule
|
|
62
|
+
firing — that is `find_referencing_symbols`. `grep` then `Edit` to delete
|
|
63
|
+
dead code is the rule firing — that is `safe_delete_symbol`. The grep
|
|
64
|
+
version of any of these is noisier and less complete: grep misses
|
|
65
|
+
re-exports, and adds false positives from comments, strings, and unrelated
|
|
66
|
+
attribute lookups on other types. The language server does not.
|
|
67
|
+
|
|
68
|
+
### How to execute the rule
|
|
69
|
+
|
|
70
|
+
The index has two parts (a namespace map and per-file stubs) and three
|
|
71
|
+
steps (orient, understand, act).
|
|
72
|
+
|
|
73
|
+
**Step 1 — Orient. Read the namespace map first.** Before answering the
|
|
74
|
+
user, before any other tool call:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Read .uncoded/namespace.yaml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This lists every symbol in the codebase — directories, files, classes,
|
|
81
|
+
methods, functions — in source order. Without it loaded, "find X"
|
|
82
|
+
answers come from pretrained guesses about what a project like this
|
|
83
|
+
*probably* contains, rather than what is actually here. Read it once,
|
|
84
|
+
in full, at session start.
|
|
85
|
+
|
|
86
|
+
**Step 2 — Understand. Read the `.pyi` stub before any `.py` source.**
|
|
87
|
+
Stub paths mirror source paths under `.uncoded/stubs/`:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
91
|
+
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every file you intend to touch or reference, including tests. The stub
|
|
95
|
+
contains imports, every signature with types, module-level assignments,
|
|
96
|
+
class attributes, and first-sentence docstrings — enough for most
|
|
97
|
+
navigation. Skipping straight to source means reading many lines to
|
|
98
|
+
learn what the stub would have told you in one. If no stub exists at
|
|
99
|
+
the expected path, the file has no symbols indexed; in that narrow
|
|
100
|
+
case, read source directly.
|
|
101
|
+
|
|
102
|
+
**Step 3 — Act. Use Serena to find, read, rename, edit, and delete symbols.**
|
|
103
|
+
With the map and stub loaded, you have the exact `relative_path` and
|
|
104
|
+
`name_path` each Serena tool needs (`ClassName/method` for a method,
|
|
105
|
+
`function_name` for a top-level function). Per task:
|
|
106
|
+
|
|
107
|
+
- **Find a symbol's definition, or read its body.** `find_symbol` —
|
|
108
|
+
with `include_body=True` when you need implementation detail the
|
|
109
|
+
stub does not show. Returns exactly the symbol; no offset arithmetic,
|
|
110
|
+
no risk of reading too much. Stay on stubs for a wider sweep.
|
|
111
|
+
|
|
112
|
+
- **Find callers, or check whether a symbol is dead.**
|
|
113
|
+
`find_referencing_symbols`. Returns every reference resolved by the
|
|
114
|
+
language server — every import, every call site, every re-export.
|
|
115
|
+
Grep on the name misses re-exports and adds false positives from
|
|
116
|
+
comments, strings, and attribute lookups on other types. If the
|
|
117
|
+
next move depends on the answer being complete, grep cannot give
|
|
118
|
+
you that.
|
|
119
|
+
|
|
120
|
+
- **Rename.** `rename_symbol`. Updates every reference across the
|
|
121
|
+
codebase in one call. Multi-file find-and-replace misses imports
|
|
122
|
+
and re-exports and racks up substring false positives.
|
|
123
|
+
|
|
124
|
+
- **Edit a symbol as a unit.** `replace_symbol_body`,
|
|
125
|
+
`insert_before_symbol`, `insert_after_symbol`. Immune to the Edit
|
|
126
|
+
tool's "string not unique" failure mode, never modify a similarly
|
|
127
|
+
named neighbour, and keep surrounding indentation consistent.
|
|
128
|
+
|
|
129
|
+
- **Delete a symbol.** `safe_delete_symbol` — not Edit, and not Edit
|
|
130
|
+
after a manual reference check. The tool fuses two operations: it
|
|
131
|
+
finds every reference, refuses to delete if any are live, and
|
|
132
|
+
removes the symbol cleanly only when it is truly dead. The two-step
|
|
133
|
+
version (find references with grep or `find_referencing_symbols`,
|
|
134
|
+
then Edit) can drift — the reference check goes stale the moment
|
|
135
|
+
any file changes between the calls. Whenever the task is "remove
|
|
136
|
+
this symbol," regardless of how dead it looks, this is the tool.
|
|
137
|
+
|
|
138
|
+
### Where Read, Edit, and grep are still the right tools
|
|
139
|
+
|
|
140
|
+
The rule is about source navigation by symbol name. Outside that, the
|
|
141
|
+
non-Serena tools stay correct:
|
|
142
|
+
|
|
143
|
+
- Free-text or pattern search outside source: Markdown, YAML, TOML,
|
|
144
|
+
configs, commit messages, notebook JSON, fixture data.
|
|
145
|
+
- Pattern search across signatures (regex over type annotations,
|
|
146
|
+
decorator usage, alias declarations) — these are not symbol-name
|
|
147
|
+
lookups even though they sit inside source.
|
|
148
|
+
- Partial-line edits inside a symbol body, once you have retrieved it
|
|
149
|
+
through Serena.
|
|
150
|
+
- Environments where Serena is unavailable, or the rare stub-less
|
|
151
|
+
Python file that needs exploratory reading.
|
|
152
|
+
|
|
153
|
+
The dispatch test is the search term: a symbol name → Serena; a regex
|
|
154
|
+
or free-text phrase → grep.
|
|
155
|
+
<!-- uncoded:end -->
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Maintain the uncoded navigation section in agent instruction files.
|
|
2
|
+
|
|
3
|
+
Different coding agents read different instruction files from a repo's root.
|
|
4
|
+
Claude Code reads ``CLAUDE.md``; an emerging cross-agent convention uses
|
|
5
|
+
``AGENTS.md``. Until the ecosystem converges, a project that wants to support
|
|
6
|
+
both populations needs both files, with the same navigation guidance in each.
|
|
7
|
+
This module owns a delimited section in any such file and keeps it in sync.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from uncoded.sync import sync_file
|
|
13
|
+
|
|
14
|
+
MARKER_START = "<!-- uncoded:start -->"
|
|
15
|
+
MARKER_END = "<!-- uncoded:end -->"
|
|
16
|
+
|
|
17
|
+
DEFAULT_INSTRUCTION_FILES = [Path("CLAUDE.md"), Path("AGENTS.md")]
|
|
18
|
+
|
|
19
|
+
_SECTION_BODY = """\
|
|
20
|
+
## How to read and edit code in this codebase
|
|
21
|
+
|
|
22
|
+
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
23
|
+
a symbol index over its source code, with
|
|
24
|
+
[Serena](https://github.com/oraios/serena) providing language-server-backed
|
|
25
|
+
tools over that index. The point of this scaffolding is one rule.
|
|
26
|
+
|
|
27
|
+
### The rule
|
|
28
|
+
|
|
29
|
+
**If your search term is the name of a Python symbol — a class, function,
|
|
30
|
+
method, attribute, or module-level constant — use the index. If it's a
|
|
31
|
+
pattern, regex, or free-text phrase, use grep.**
|
|
32
|
+
|
|
33
|
+
This applies to every tool call where you are trying to find code, not
|
|
34
|
+
just the first one in the session. The pretrained reflex for "find X" is
|
|
35
|
+
grep, and that reflex is wrong here. `grep -rn 'WriteResult'` to locate a
|
|
36
|
+
class is the rule firing — that is `find_symbol`. `grep -rn 'function_name'`
|
|
37
|
+
to check whether something has callers before a refactor is the rule
|
|
38
|
+
firing — that is `find_referencing_symbols`. `grep` then `Edit` to delete
|
|
39
|
+
dead code is the rule firing — that is `safe_delete_symbol`. The grep
|
|
40
|
+
version of any of these is noisier and less complete: grep misses
|
|
41
|
+
re-exports, and adds false positives from comments, strings, and unrelated
|
|
42
|
+
attribute lookups on other types. The language server does not.
|
|
43
|
+
|
|
44
|
+
### How to execute the rule
|
|
45
|
+
|
|
46
|
+
The index has two parts (a namespace map and per-file stubs) and three
|
|
47
|
+
steps (orient, understand, act).
|
|
48
|
+
|
|
49
|
+
**Step 1 — Orient. Read the namespace map first.** Before answering the
|
|
50
|
+
user, before any other tool call:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Read .uncoded/namespace.yaml
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This lists every symbol in the codebase — directories, files, classes,
|
|
57
|
+
methods, functions — in source order. Without it loaded, "find X"
|
|
58
|
+
answers come from pretrained guesses about what a project like this
|
|
59
|
+
*probably* contains, rather than what is actually here. Read it once,
|
|
60
|
+
in full, at session start.
|
|
61
|
+
|
|
62
|
+
**Step 2 — Understand. Read the `.pyi` stub before any `.py` source.**
|
|
63
|
+
Stub paths mirror source paths under `.uncoded/stubs/`:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
67
|
+
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Every file you intend to touch or reference, including tests. The stub
|
|
71
|
+
contains imports, every signature with types, module-level assignments,
|
|
72
|
+
class attributes, and first-sentence docstrings — enough for most
|
|
73
|
+
navigation. Skipping straight to source means reading many lines to
|
|
74
|
+
learn what the stub would have told you in one. If no stub exists at
|
|
75
|
+
the expected path, the file has no symbols indexed; in that narrow
|
|
76
|
+
case, read source directly.
|
|
77
|
+
|
|
78
|
+
**Step 3 — Act. Use Serena to find, read, rename, edit, and delete symbols.**
|
|
79
|
+
With the map and stub loaded, you have the exact `relative_path` and
|
|
80
|
+
`name_path` each Serena tool needs (`ClassName/method` for a method,
|
|
81
|
+
`function_name` for a top-level function). Per task:
|
|
82
|
+
|
|
83
|
+
- **Find a symbol's definition, or read its body.** `find_symbol` —
|
|
84
|
+
with `include_body=True` when you need implementation detail the
|
|
85
|
+
stub does not show. Returns exactly the symbol; no offset arithmetic,
|
|
86
|
+
no risk of reading too much. Stay on stubs for a wider sweep.
|
|
87
|
+
|
|
88
|
+
- **Find callers, or check whether a symbol is dead.**
|
|
89
|
+
`find_referencing_symbols`. Returns every reference resolved by the
|
|
90
|
+
language server — every import, every call site, every re-export.
|
|
91
|
+
Grep on the name misses re-exports and adds false positives from
|
|
92
|
+
comments, strings, and attribute lookups on other types. If the
|
|
93
|
+
next move depends on the answer being complete, grep cannot give
|
|
94
|
+
you that.
|
|
95
|
+
|
|
96
|
+
- **Rename.** `rename_symbol`. Updates every reference across the
|
|
97
|
+
codebase in one call. Multi-file find-and-replace misses imports
|
|
98
|
+
and re-exports and racks up substring false positives.
|
|
99
|
+
|
|
100
|
+
- **Edit a symbol as a unit.** `replace_symbol_body`,
|
|
101
|
+
`insert_before_symbol`, `insert_after_symbol`. Immune to the Edit
|
|
102
|
+
tool's "string not unique" failure mode, never modify a similarly
|
|
103
|
+
named neighbour, and keep surrounding indentation consistent.
|
|
104
|
+
|
|
105
|
+
- **Delete a symbol.** `safe_delete_symbol` — not Edit, and not Edit
|
|
106
|
+
after a manual reference check. The tool fuses two operations: it
|
|
107
|
+
finds every reference, refuses to delete if any are live, and
|
|
108
|
+
removes the symbol cleanly only when it is truly dead. The two-step
|
|
109
|
+
version (find references with grep or `find_referencing_symbols`,
|
|
110
|
+
then Edit) can drift — the reference check goes stale the moment
|
|
111
|
+
any file changes between the calls. Whenever the task is "remove
|
|
112
|
+
this symbol," regardless of how dead it looks, this is the tool.
|
|
113
|
+
|
|
114
|
+
### Where Read, Edit, and grep are still the right tools
|
|
115
|
+
|
|
116
|
+
The rule is about source navigation by symbol name. Outside that, the
|
|
117
|
+
non-Serena tools stay correct:
|
|
118
|
+
|
|
119
|
+
- Free-text or pattern search outside source: Markdown, YAML, TOML,
|
|
120
|
+
configs, commit messages, notebook JSON, fixture data.
|
|
121
|
+
- Pattern search across signatures (regex over type annotations,
|
|
122
|
+
decorator usage, alias declarations) — these are not symbol-name
|
|
123
|
+
lookups even though they sit inside source.
|
|
124
|
+
- Partial-line edits inside a symbol body, once you have retrieved it
|
|
125
|
+
through Serena.
|
|
126
|
+
- Environments where Serena is unavailable, or the rare stub-less
|
|
127
|
+
Python file that needs exploratory reading.
|
|
128
|
+
|
|
129
|
+
The dispatch test is the search term: a symbol name → Serena; a regex
|
|
130
|
+
or free-text phrase → grep."""
|
|
131
|
+
|
|
132
|
+
SECTION = f"{MARKER_START}\n{_SECTION_BODY}\n{MARKER_END}\n"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def generate_section() -> str:
|
|
136
|
+
"""Return the full delimited uncoded section for an instruction file."""
|
|
137
|
+
return SECTION
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _replace_or_append(existing: str, section: str) -> str:
|
|
141
|
+
"""Replace the delimited section in existing text, or append it if absent."""
|
|
142
|
+
start = existing.find(MARKER_START)
|
|
143
|
+
end = existing.find(MARKER_END)
|
|
144
|
+
if start != -1 and end != -1 and start < end:
|
|
145
|
+
before = existing[:start]
|
|
146
|
+
after = existing[end + len(MARKER_END) :].lstrip("\n")
|
|
147
|
+
return before + section + after
|
|
148
|
+
stripped = existing.rstrip("\n")
|
|
149
|
+
prefix = stripped + "\n\n" if stripped else ""
|
|
150
|
+
return prefix + section
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def sync_instruction_file(path: Path, *, check: bool = False) -> bool:
|
|
154
|
+
"""Write or update the uncoded navigation section in an instruction file.
|
|
155
|
+
|
|
156
|
+
When ``check=True``, reports a prospective change without touching disk.
|
|
157
|
+
Returns ``True`` if a write was (or would be) performed.
|
|
158
|
+
"""
|
|
159
|
+
section = generate_section()
|
|
160
|
+
if not path.exists():
|
|
161
|
+
return sync_file(path, section, check=check)
|
|
162
|
+
existing = path.read_text()
|
|
163
|
+
updated = _replace_or_append(existing, section)
|
|
164
|
+
return sync_file(path, updated, check=check)
|
uncoded-0.5.0/AGENTS.md
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# uncoded
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
AI coding agents navigate codebases poorly. They grep for guessed keywords,
|
|
6
|
-
skim the first few lines of files, and fill gaps from pretraining rather than
|
|
7
|
-
reading the actual code. System prompts encourage this with "make reasonable
|
|
8
|
-
assumptions." The result is plausible-looking output built on a hallucinated
|
|
9
|
-
understanding of code that's sitting right there, unread.
|
|
10
|
-
|
|
11
|
-
## Approach
|
|
12
|
-
|
|
13
|
-
A static, pre-computed index gives agents a top-down view of a codebase.
|
|
14
|
-
The agent loads the index at the start of a task, sees the full vocabulary
|
|
15
|
-
of the code, and navigates deterministically to what it needs — no guessing,
|
|
16
|
-
no grep.
|
|
17
|
-
|
|
18
|
-
Two-level index:
|
|
19
|
-
|
|
20
|
-
1. **Namespace map** (`.uncoded/namespace.yaml`) — a hierarchical YAML file
|
|
21
|
-
listing all symbols: directories, files, classes (with attributes
|
|
22
|
-
and methods), and functions. Covers both source and tests. Loaded into
|
|
23
|
-
context before any task begins. Gives the agent a world view.
|
|
24
|
-
|
|
25
|
-
2. **Stub files** (`.uncoded/stubs/`) — one `.pyi` per source file, with
|
|
26
|
-
imports, full signatures (parameter names, types, return types),
|
|
27
|
-
first-sentence docstrings, module constants, and class attributes.
|
|
28
|
-
|
|
29
|
-
## Commands
|
|
30
|
-
|
|
31
|
-
This project uses [uv](https://docs.astral.sh/uv/). Run commands via
|
|
32
|
-
`uv run` so they execute inside the project environment without needing
|
|
33
|
-
an activated venv.
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
# Generate (or update) the namespace map, stub files, and CLAUDE.md section
|
|
37
|
-
uv run uncoded sync
|
|
38
|
-
|
|
39
|
-
# Run tests
|
|
40
|
-
uv run pytest
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
<!-- uncoded:start -->
|
|
44
|
-
## How to read and edit code in this codebase
|
|
45
|
-
|
|
46
|
-
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
47
|
-
a symbol index over its source code, designed for AI agents to navigate
|
|
48
|
-
deterministically rather than by grep-and-skim. For source navigation, use
|
|
49
|
-
the index — grep-and-skim produces a noisier, slower version of what the
|
|
50
|
-
index already lists in full. (For free-text search elsewhere —
|
|
51
|
-
Markdown, configs, commit messages — grep remains the right tool. The index
|
|
52
|
-
is about source.)
|
|
53
|
-
|
|
54
|
-
**Step 1 — Orient. Do this now, before anything else.** Your first action
|
|
55
|
-
in this session — before answering the user, before any other tool call —
|
|
56
|
-
is to read the namespace map in full:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
Read .uncoded/namespace.yaml
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Do this once, immediately, at session start — not "eventually" or "when
|
|
63
|
-
a code question comes up." Without the map loaded, design and navigation
|
|
64
|
-
answers will come from pretrained guesses rather than the code actually
|
|
65
|
-
here. The map lists every symbol in the codebase — directories, files,
|
|
66
|
-
classes, methods, functions — in source order.
|
|
67
|
-
|
|
68
|
-
**Step 2 — Understand.** Before reading any `.py` source file in this repo,
|
|
69
|
-
read its `.pyi` stub first. Stub paths mirror source paths under
|
|
70
|
-
`.uncoded/stubs/`:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
74
|
-
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This applies to every file you intend to touch or reference — including
|
|
78
|
-
tests. The stub is sufficient for most navigation: it contains imports,
|
|
79
|
-
every signature with types, module-level assignments, class attributes,
|
|
80
|
-
and first-sentence docstrings. Skipping to source means reading many
|
|
81
|
-
lines to learn what the stub would have told you in one. If no stub
|
|
82
|
-
exists at the expected path, the file has no symbols indexed — in that
|
|
83
|
-
narrow case, read source directly.
|
|
84
|
-
|
|
85
|
-
**Step 3 — Read source through Serena.** When you need source beyond
|
|
86
|
-
what the stub shows, use Serena to read exactly the symbol body. The
|
|
87
|
-
namespace map and stub give you the exact `relative_path` and `name_path`
|
|
88
|
-
Serena needs — e.g. `ClassName/method` for a method,
|
|
89
|
-
`function_name` for a top-level function.
|
|
90
|
-
|
|
91
|
-
Stubs intentionally do not carry source line ranges: line numbers churn
|
|
92
|
-
when nearby code moves, creating noisy generated diffs and teaching
|
|
93
|
-
agents to trust stale coordinates. Let uncoded provide the stable map
|
|
94
|
-
and signatures; let Serena resolve the current source body.
|
|
95
|
-
|
|
96
|
-
**For symbol-level operations — use Serena.** Where Serena's MCP tools
|
|
97
|
-
are available (`mcp__serena__*` in the tool list), prefer them over
|
|
98
|
-
Read / Edit / grep for anything that operates on a symbol as a unit.
|
|
99
|
-
|
|
100
|
-
- **Read one symbol body.** `find_symbol` with `include_body=True`
|
|
101
|
-
returns exactly the symbol — no offset arithmetic, no risk of reading
|
|
102
|
-
too much. Stay on stubs for a wider sweep; use Serena when you need
|
|
103
|
-
implementation detail.
|
|
104
|
-
- **Find callers, or check whether a symbol is dead.**
|
|
105
|
-
`find_referencing_symbols` returns every reference resolved by the
|
|
106
|
-
language server. Do not grep for the name — grep hits comments,
|
|
107
|
-
strings, attribute lookups on unrelated types, and re-exports.
|
|
108
|
-
- **Rename.** `rename_symbol` updates every reference across the
|
|
109
|
-
codebase in one call. Multi-file find-and-replace misses imports
|
|
110
|
-
and re-exports and racks up false positives.
|
|
111
|
-
- **Edit a whole symbol.** `replace_symbol_body`,
|
|
112
|
-
`insert_before_symbol`, and `insert_after_symbol` operate on the
|
|
113
|
-
symbol as a unit. Immune to the Edit tool's "string not unique"
|
|
114
|
-
failure mode, never accidentally modify a similarly-named
|
|
115
|
-
neighbour, and keep surrounding indentation consistent.
|
|
116
|
-
- **Delete a symbol.** `safe_delete_symbol` checks for live
|
|
117
|
-
references before removing — dead code goes cleanly, live code
|
|
118
|
-
stays put.
|
|
119
|
-
|
|
120
|
-
Reach for Read + Edit when Serena does not fit: free-text files
|
|
121
|
-
(Markdown, YAML, configs), partial-line edits inside a function body
|
|
122
|
-
after you have retrieved it, environments where Serena is unavailable,
|
|
123
|
-
or the rare stub-less Python file that needs exploratory reading.
|
|
124
|
-
<!-- uncoded:end -->
|
uncoded-0.5.0/CLAUDE.md
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# uncoded
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
AI coding agents navigate codebases poorly. They grep for guessed keywords,
|
|
6
|
-
skim the first few lines of files, and fill gaps from pretraining rather than
|
|
7
|
-
reading the actual code. System prompts encourage this with "make reasonable
|
|
8
|
-
assumptions." The result is plausible-looking output built on a hallucinated
|
|
9
|
-
understanding of code that's sitting right there, unread.
|
|
10
|
-
|
|
11
|
-
## Approach
|
|
12
|
-
|
|
13
|
-
A static, pre-computed index gives agents a top-down view of a codebase.
|
|
14
|
-
The agent loads the index at the start of a task, sees the full vocabulary
|
|
15
|
-
of the code, and navigates deterministically to what it needs — no guessing,
|
|
16
|
-
no grep.
|
|
17
|
-
|
|
18
|
-
Two-level index:
|
|
19
|
-
|
|
20
|
-
1. **Namespace map** (`.uncoded/namespace.yaml`) — a hierarchical YAML file
|
|
21
|
-
listing all symbols: directories, files, classes (with attributes
|
|
22
|
-
and methods), and functions. Covers both source and tests. Loaded into
|
|
23
|
-
context before any task begins. Gives the agent a world view.
|
|
24
|
-
|
|
25
|
-
2. **Stub files** (`.uncoded/stubs/`) — one `.pyi` per source file, with
|
|
26
|
-
imports, full signatures (parameter names, types, return types),
|
|
27
|
-
first-sentence docstrings, module constants, and class attributes.
|
|
28
|
-
|
|
29
|
-
## Commands
|
|
30
|
-
|
|
31
|
-
This project uses [uv](https://docs.astral.sh/uv/). Run commands via
|
|
32
|
-
`uv run` so they execute inside the project environment without needing
|
|
33
|
-
an activated venv.
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
# Generate (or update) the namespace map, stub files, and CLAUDE.md section
|
|
37
|
-
uv run uncoded sync
|
|
38
|
-
|
|
39
|
-
# Run tests
|
|
40
|
-
uv run pytest
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
<!-- uncoded:start -->
|
|
44
|
-
## How to read and edit code in this codebase
|
|
45
|
-
|
|
46
|
-
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
47
|
-
a symbol index over its source code, designed for AI agents to navigate
|
|
48
|
-
deterministically rather than by grep-and-skim. For source navigation, use
|
|
49
|
-
the index — grep-and-skim produces a noisier, slower version of what the
|
|
50
|
-
index already lists in full. (For free-text search elsewhere —
|
|
51
|
-
Markdown, configs, commit messages — grep remains the right tool. The index
|
|
52
|
-
is about source.)
|
|
53
|
-
|
|
54
|
-
**Step 1 — Orient. Do this now, before anything else.** Your first action
|
|
55
|
-
in this session — before answering the user, before any other tool call —
|
|
56
|
-
is to read the namespace map in full:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
Read .uncoded/namespace.yaml
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Do this once, immediately, at session start — not "eventually" or "when
|
|
63
|
-
a code question comes up." Without the map loaded, design and navigation
|
|
64
|
-
answers will come from pretrained guesses rather than the code actually
|
|
65
|
-
here. The map lists every symbol in the codebase — directories, files,
|
|
66
|
-
classes, methods, functions — in source order.
|
|
67
|
-
|
|
68
|
-
**Step 2 — Understand.** Before reading any `.py` source file in this repo,
|
|
69
|
-
read its `.pyi` stub first. Stub paths mirror source paths under
|
|
70
|
-
`.uncoded/stubs/`:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
74
|
-
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This applies to every file you intend to touch or reference — including
|
|
78
|
-
tests. The stub is sufficient for most navigation: it contains imports,
|
|
79
|
-
every signature with types, module-level assignments, class attributes,
|
|
80
|
-
and first-sentence docstrings. Skipping to source means reading many
|
|
81
|
-
lines to learn what the stub would have told you in one. If no stub
|
|
82
|
-
exists at the expected path, the file has no symbols indexed — in that
|
|
83
|
-
narrow case, read source directly.
|
|
84
|
-
|
|
85
|
-
**Step 3 — Read source through Serena.** When you need source beyond
|
|
86
|
-
what the stub shows, use Serena to read exactly the symbol body. The
|
|
87
|
-
namespace map and stub give you the exact `relative_path` and `name_path`
|
|
88
|
-
Serena needs — e.g. `ClassName/method` for a method,
|
|
89
|
-
`function_name` for a top-level function.
|
|
90
|
-
|
|
91
|
-
Stubs intentionally do not carry source line ranges: line numbers churn
|
|
92
|
-
when nearby code moves, creating noisy generated diffs and teaching
|
|
93
|
-
agents to trust stale coordinates. Let uncoded provide the stable map
|
|
94
|
-
and signatures; let Serena resolve the current source body.
|
|
95
|
-
|
|
96
|
-
**For symbol-level operations — use Serena.** Where Serena's MCP tools
|
|
97
|
-
are available (`mcp__serena__*` in the tool list), prefer them over
|
|
98
|
-
Read / Edit / grep for anything that operates on a symbol as a unit.
|
|
99
|
-
|
|
100
|
-
- **Read one symbol body.** `find_symbol` with `include_body=True`
|
|
101
|
-
returns exactly the symbol — no offset arithmetic, no risk of reading
|
|
102
|
-
too much. Stay on stubs for a wider sweep; use Serena when you need
|
|
103
|
-
implementation detail.
|
|
104
|
-
- **Find callers, or check whether a symbol is dead.**
|
|
105
|
-
`find_referencing_symbols` returns every reference resolved by the
|
|
106
|
-
language server. Do not grep for the name — grep hits comments,
|
|
107
|
-
strings, attribute lookups on unrelated types, and re-exports.
|
|
108
|
-
- **Rename.** `rename_symbol` updates every reference across the
|
|
109
|
-
codebase in one call. Multi-file find-and-replace misses imports
|
|
110
|
-
and re-exports and racks up false positives.
|
|
111
|
-
- **Edit a whole symbol.** `replace_symbol_body`,
|
|
112
|
-
`insert_before_symbol`, and `insert_after_symbol` operate on the
|
|
113
|
-
symbol as a unit. Immune to the Edit tool's "string not unique"
|
|
114
|
-
failure mode, never accidentally modify a similarly-named
|
|
115
|
-
neighbour, and keep surrounding indentation consistent.
|
|
116
|
-
- **Delete a symbol.** `safe_delete_symbol` checks for live
|
|
117
|
-
references before removing — dead code goes cleanly, live code
|
|
118
|
-
stays put.
|
|
119
|
-
|
|
120
|
-
Reach for Read + Edit when Serena does not fit: free-text files
|
|
121
|
-
(Markdown, YAML, configs), partial-line edits inside a function body
|
|
122
|
-
after you have retrieved it, environments where Serena is unavailable,
|
|
123
|
-
or the rare stub-less Python file that needs exploratory reading.
|
|
124
|
-
<!-- uncoded:end -->
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
"""Maintain the uncoded navigation section in agent instruction files.
|
|
2
|
-
|
|
3
|
-
Different coding agents read different instruction files from a repo's root.
|
|
4
|
-
Claude Code reads ``CLAUDE.md``; an emerging cross-agent convention uses
|
|
5
|
-
``AGENTS.md``. Until the ecosystem converges, a project that wants to support
|
|
6
|
-
both populations needs both files, with the same navigation guidance in each.
|
|
7
|
-
This module owns a delimited section in any such file and keeps it in sync.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from uncoded.sync import sync_file
|
|
13
|
-
|
|
14
|
-
MARKER_START = "<!-- uncoded:start -->"
|
|
15
|
-
MARKER_END = "<!-- uncoded:end -->"
|
|
16
|
-
|
|
17
|
-
DEFAULT_INSTRUCTION_FILES = [Path("CLAUDE.md"), Path("AGENTS.md")]
|
|
18
|
-
|
|
19
|
-
_SECTION_BODY = """\
|
|
20
|
-
## How to read and edit code in this codebase
|
|
21
|
-
|
|
22
|
-
This repo uses [uncoded](https://github.com/alimanfoo/uncoded) to maintain
|
|
23
|
-
a symbol index over its source code, designed for AI agents to navigate
|
|
24
|
-
deterministically rather than by grep-and-skim. For source navigation, use
|
|
25
|
-
the index — grep-and-skim produces a noisier, slower version of what the
|
|
26
|
-
index already lists in full. (For free-text search elsewhere —
|
|
27
|
-
Markdown, configs, commit messages — grep remains the right tool. The index
|
|
28
|
-
is about source.)
|
|
29
|
-
|
|
30
|
-
**Step 1 — Orient. Do this now, before anything else.** Your first action
|
|
31
|
-
in this session — before answering the user, before any other tool call —
|
|
32
|
-
is to read the namespace map in full:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
Read .uncoded/namespace.yaml
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Do this once, immediately, at session start — not "eventually" or "when
|
|
39
|
-
a code question comes up." Without the map loaded, design and navigation
|
|
40
|
-
answers will come from pretrained guesses rather than the code actually
|
|
41
|
-
here. The map lists every symbol in the codebase — directories, files,
|
|
42
|
-
classes, methods, functions — in source order.
|
|
43
|
-
|
|
44
|
-
**Step 2 — Understand.** Before reading any `.py` source file in this repo,
|
|
45
|
-
read its `.pyi` stub first. Stub paths mirror source paths under
|
|
46
|
-
`.uncoded/stubs/`:
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
50
|
-
tests/test_foo.py → .uncoded/stubs/tests/test_foo.pyi
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
This applies to every file you intend to touch or reference — including
|
|
54
|
-
tests. The stub is sufficient for most navigation: it contains imports,
|
|
55
|
-
every signature with types, module-level assignments, class attributes,
|
|
56
|
-
and first-sentence docstrings. Skipping to source means reading many
|
|
57
|
-
lines to learn what the stub would have told you in one. If no stub
|
|
58
|
-
exists at the expected path, the file has no symbols indexed — in that
|
|
59
|
-
narrow case, read source directly.
|
|
60
|
-
|
|
61
|
-
**Step 3 — Read source through Serena.** When you need source beyond
|
|
62
|
-
what the stub shows, use Serena to read exactly the symbol body. The
|
|
63
|
-
namespace map and stub give you the exact `relative_path` and `name_path`
|
|
64
|
-
Serena needs — e.g. `ClassName/method` for a method,
|
|
65
|
-
`function_name` for a top-level function.
|
|
66
|
-
|
|
67
|
-
Stubs intentionally do not carry source line ranges: line numbers churn
|
|
68
|
-
when nearby code moves, creating noisy generated diffs and teaching
|
|
69
|
-
agents to trust stale coordinates. Let uncoded provide the stable map
|
|
70
|
-
and signatures; let Serena resolve the current source body.
|
|
71
|
-
|
|
72
|
-
**For symbol-level operations — use Serena.** Where Serena's MCP tools
|
|
73
|
-
are available (`mcp__serena__*` in the tool list), prefer them over
|
|
74
|
-
Read / Edit / grep for anything that operates on a symbol as a unit.
|
|
75
|
-
|
|
76
|
-
- **Read one symbol body.** `find_symbol` with `include_body=True`
|
|
77
|
-
returns exactly the symbol — no offset arithmetic, no risk of reading
|
|
78
|
-
too much. Stay on stubs for a wider sweep; use Serena when you need
|
|
79
|
-
implementation detail.
|
|
80
|
-
- **Find callers, or check whether a symbol is dead.**
|
|
81
|
-
`find_referencing_symbols` returns every reference resolved by the
|
|
82
|
-
language server. Do not grep for the name — grep hits comments,
|
|
83
|
-
strings, attribute lookups on unrelated types, and re-exports.
|
|
84
|
-
- **Rename.** `rename_symbol` updates every reference across the
|
|
85
|
-
codebase in one call. Multi-file find-and-replace misses imports
|
|
86
|
-
and re-exports and racks up false positives.
|
|
87
|
-
- **Edit a whole symbol.** `replace_symbol_body`,
|
|
88
|
-
`insert_before_symbol`, and `insert_after_symbol` operate on the
|
|
89
|
-
symbol as a unit. Immune to the Edit tool's "string not unique"
|
|
90
|
-
failure mode, never accidentally modify a similarly-named
|
|
91
|
-
neighbour, and keep surrounding indentation consistent.
|
|
92
|
-
- **Delete a symbol.** `safe_delete_symbol` checks for live
|
|
93
|
-
references before removing — dead code goes cleanly, live code
|
|
94
|
-
stays put.
|
|
95
|
-
|
|
96
|
-
Reach for Read + Edit when Serena does not fit: free-text files
|
|
97
|
-
(Markdown, YAML, configs), partial-line edits inside a function body
|
|
98
|
-
after you have retrieved it, environments where Serena is unavailable,
|
|
99
|
-
or the rare stub-less Python file that needs exploratory reading."""
|
|
100
|
-
|
|
101
|
-
SECTION = f"{MARKER_START}\n{_SECTION_BODY}\n{MARKER_END}\n"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def generate_section() -> str:
|
|
105
|
-
"""Return the full delimited uncoded section for an instruction file."""
|
|
106
|
-
return SECTION
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def _replace_or_append(existing: str, section: str) -> str:
|
|
110
|
-
"""Replace the delimited section in existing text, or append it if absent."""
|
|
111
|
-
start = existing.find(MARKER_START)
|
|
112
|
-
end = existing.find(MARKER_END)
|
|
113
|
-
if start != -1 and end != -1 and start < end:
|
|
114
|
-
before = existing[:start]
|
|
115
|
-
after = existing[end + len(MARKER_END) :].lstrip("\n")
|
|
116
|
-
return before + section + after
|
|
117
|
-
stripped = existing.rstrip("\n")
|
|
118
|
-
prefix = stripped + "\n\n" if stripped else ""
|
|
119
|
-
return prefix + section
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def sync_instruction_file(path: Path, *, check: bool = False) -> bool:
|
|
123
|
-
"""Write or update the uncoded navigation section in an instruction file.
|
|
124
|
-
|
|
125
|
-
When ``check=True``, reports a prospective change without touching disk.
|
|
126
|
-
Returns ``True`` if a write was (or would be) performed.
|
|
127
|
-
"""
|
|
128
|
-
section = generate_section()
|
|
129
|
-
if not path.exists():
|
|
130
|
-
return sync_file(path, section, check=check)
|
|
131
|
-
existing = path.read_text()
|
|
132
|
-
updated = _replace_or_append(existing, section)
|
|
133
|
-
return sync_file(path, updated, check=check)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|