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.
Files changed (65) hide show
  1. uncoded-0.6.0/AGENTS.md +155 -0
  2. uncoded-0.6.0/CLAUDE.md +155 -0
  3. {uncoded-0.5.0 → uncoded-0.6.0}/PKG-INFO +1 -1
  4. uncoded-0.6.0/src/uncoded/instruction_files.py +164 -0
  5. uncoded-0.5.0/AGENTS.md +0 -124
  6. uncoded-0.5.0/CLAUDE.md +0 -124
  7. uncoded-0.5.0/src/uncoded/instruction_files.py +0 -133
  8. {uncoded-0.5.0 → uncoded-0.6.0}/.agents/skills/coherence-review/SKILL.md +0 -0
  9. {uncoded-0.5.0 → uncoded-0.6.0}/.claude/settings.json +0 -0
  10. {uncoded-0.5.0 → uncoded-0.6.0}/.claude/skills/coherence-review/SKILL.md +0 -0
  11. {uncoded-0.5.0 → uncoded-0.6.0}/.github/workflows/ci.yml +0 -0
  12. {uncoded-0.5.0 → uncoded-0.6.0}/.github/workflows/publish.yml +0 -0
  13. {uncoded-0.5.0 → uncoded-0.6.0}/.gitignore +0 -0
  14. {uncoded-0.5.0 → uncoded-0.6.0}/.mcp.json +0 -0
  15. {uncoded-0.5.0 → uncoded-0.6.0}/.pre-commit-config.yaml +0 -0
  16. {uncoded-0.5.0 → uncoded-0.6.0}/.serena/project.yml +0 -0
  17. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/namespace.yaml +0 -0
  18. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/reviews/2026-04-25-001215.md +0 -0
  19. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/__init__.pyi +0 -0
  20. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/cli.pyi +0 -0
  21. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/config.pyi +0 -0
  22. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/extract.pyi +0 -0
  23. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/instruction_files.pyi +0 -0
  24. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/namespace_map.pyi +0 -0
  25. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/serena_setup.pyi +0 -0
  26. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/skill.pyi +0 -0
  27. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/stubs.pyi +0 -0
  28. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/src/uncoded/sync.pyi +0 -0
  29. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_cli.pyi +0 -0
  30. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_config.pyi +0 -0
  31. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_extract.pyi +0 -0
  32. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_instruction_files.pyi +0 -0
  33. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_namespace_map.pyi +0 -0
  34. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_serena_setup.pyi +0 -0
  35. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_skill.pyi +0 -0
  36. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_stubs.pyi +0 -0
  37. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_sync.pyi +0 -0
  38. {uncoded-0.5.0 → uncoded-0.6.0}/.uncoded/stubs/tests/test_uncoded.pyi +0 -0
  39. {uncoded-0.5.0 → uncoded-0.6.0}/LICENSE +0 -0
  40. {uncoded-0.5.0 → uncoded-0.6.0}/MAINTENANCE.md +0 -0
  41. {uncoded-0.5.0 → uncoded-0.6.0}/README.md +0 -0
  42. {uncoded-0.5.0 → uncoded-0.6.0}/SYSTEM.md +0 -0
  43. {uncoded-0.5.0 → uncoded-0.6.0}/pyproject.toml +0 -0
  44. {uncoded-0.5.0 → uncoded-0.6.0}/resources/lsp-research.md +0 -0
  45. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/__init__.py +0 -0
  46. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/cli.py +0 -0
  47. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/config.py +0 -0
  48. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/extract.py +0 -0
  49. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/namespace_map.py +0 -0
  50. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/serena_setup.py +0 -0
  51. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/skill.py +0 -0
  52. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/stubs.py +0 -0
  53. {uncoded-0.5.0 → uncoded-0.6.0}/src/uncoded/sync.py +0 -0
  54. {uncoded-0.5.0 → uncoded-0.6.0}/tests/__init__.py +0 -0
  55. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_cli.py +0 -0
  56. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_config.py +0 -0
  57. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_extract.py +0 -0
  58. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_instruction_files.py +0 -0
  59. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_namespace_map.py +0 -0
  60. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_serena_setup.py +0 -0
  61. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_skill.py +0 -0
  62. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_stubs.py +0 -0
  63. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_sync.py +0 -0
  64. {uncoded-0.5.0 → uncoded-0.6.0}/tests/test_uncoded.py +0 -0
  65. {uncoded-0.5.0 → uncoded-0.6.0}/uv.lock +0 -0
@@ -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,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 -->
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uncoded
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Static symbol indexes for AI-assisted code navigation
5
5
  Project-URL: Homepage, https://github.com/alimanfoo/uncoded
6
6
  Project-URL: Repository, https://github.com/alimanfoo/uncoded
@@ -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