uncoded 0.7.0__tar.gz → 1.0.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 (75) hide show
  1. {uncoded-0.7.0 → uncoded-1.0.0}/.agents/skills/coherence-review/SKILL.md +21 -22
  2. {uncoded-0.7.0 → uncoded-1.0.0}/.claude/skills/coherence-review/SKILL.md +21 -22
  3. {uncoded-0.7.0 → uncoded-1.0.0}/.github/workflows/ci.yml +1 -1
  4. {uncoded-0.7.0 → uncoded-1.0.0}/.gitignore +0 -5
  5. {uncoded-0.7.0 → uncoded-1.0.0}/.pre-commit-config.yaml +0 -5
  6. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/namespace.yaml +165 -53
  7. uncoded-1.0.0/.uncoded/stubs/src/uncoded/ast_helpers.pyi +9 -0
  8. uncoded-1.0.0/.uncoded/stubs/src/uncoded/body.pyi +11 -0
  9. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/cli.pyi +12 -1
  10. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/config.pyi +0 -3
  11. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/extract.pyi +1 -6
  12. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/instruction_files.pyi +2 -1
  13. uncoded-1.0.0/.uncoded/stubs/src/uncoded/refs.pyi +54 -0
  14. uncoded-1.0.0/.uncoded/stubs/src/uncoded/resolver.pyi +34 -0
  15. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/stubs.pyi +2 -2
  16. uncoded-1.0.0/.uncoded/stubs/tests/test_body.pyi +145 -0
  17. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_cli.pyi +72 -3
  18. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_config.pyi +1 -14
  19. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_extract.pyi +11 -2
  20. uncoded-1.0.0/.uncoded/stubs/tests/test_refs.pyi +88 -0
  21. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_stubs.pyi +23 -14
  22. {uncoded-0.7.0 → uncoded-1.0.0}/AGENTS.md +58 -64
  23. {uncoded-0.7.0 → uncoded-1.0.0}/PKG-INFO +68 -75
  24. {uncoded-0.7.0 → uncoded-1.0.0}/README.md +65 -73
  25. {uncoded-0.7.0 → uncoded-1.0.0}/pyproject.toml +12 -1
  26. uncoded-1.0.0/src/uncoded/ast_helpers.py +30 -0
  27. uncoded-1.0.0/src/uncoded/body.py +32 -0
  28. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/cli.py +122 -22
  29. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/config.py +0 -23
  30. uncoded-1.0.0/src/uncoded/dispatch_rule.md +104 -0
  31. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/extract.py +5 -30
  32. uncoded-1.0.0/src/uncoded/instruction_files.py +64 -0
  33. uncoded-1.0.0/src/uncoded/refs.py +231 -0
  34. uncoded-1.0.0/src/uncoded/resolver.py +163 -0
  35. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/skill.py +21 -22
  36. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/stubs.py +8 -9
  37. uncoded-1.0.0/tests/test_body.py +470 -0
  38. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_cli.py +256 -6
  39. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_config.py +0 -18
  40. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_extract.py +33 -2
  41. uncoded-1.0.0/tests/test_refs.py +319 -0
  42. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_stubs.py +289 -110
  43. uncoded-1.0.0/uv.lock +329 -0
  44. uncoded-0.7.0/.claude/settings.json +0 -18
  45. uncoded-0.7.0/.mcp.json +0 -20
  46. uncoded-0.7.0/.serena/project.yml +0 -166
  47. uncoded-0.7.0/.uncoded/reviews/2026-04-25-001215.md +0 -150
  48. uncoded-0.7.0/.uncoded/stubs/src/uncoded/serena_setup.pyi +0 -26
  49. uncoded-0.7.0/.uncoded/stubs/tests/test_serena_setup.pyi +0 -63
  50. uncoded-0.7.0/resources/lsp-research.md +0 -45
  51. uncoded-0.7.0/src/uncoded/instruction_files.py +0 -176
  52. uncoded-0.7.0/src/uncoded/serena_setup.py +0 -216
  53. uncoded-0.7.0/tests/test_serena_setup.py +0 -228
  54. uncoded-0.7.0/uv.lock +0 -229
  55. {uncoded-0.7.0 → uncoded-1.0.0}/.github/workflows/publish.yml +0 -0
  56. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/__init__.pyi +0 -0
  57. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/namespace_map.pyi +0 -0
  58. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/skill.pyi +0 -0
  59. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/src/uncoded/sync.pyi +0 -0
  60. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_instruction_files.pyi +0 -0
  61. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_namespace_map.pyi +0 -0
  62. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_skill.pyi +0 -0
  63. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_sync.pyi +0 -0
  64. {uncoded-0.7.0 → uncoded-1.0.0}/.uncoded/stubs/tests/test_uncoded.pyi +0 -0
  65. {uncoded-0.7.0 → uncoded-1.0.0}/CLAUDE.md +0 -0
  66. {uncoded-0.7.0 → uncoded-1.0.0}/LICENSE +0 -0
  67. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/__init__.py +0 -0
  68. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/namespace_map.py +0 -0
  69. {uncoded-0.7.0 → uncoded-1.0.0}/src/uncoded/sync.py +0 -0
  70. {uncoded-0.7.0 → uncoded-1.0.0}/tests/__init__.py +0 -0
  71. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_instruction_files.py +0 -0
  72. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_namespace_map.py +0 -0
  73. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_skill.py +0 -0
  74. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_sync.py +0 -0
  75. {uncoded-0.7.0 → uncoded-1.0.0}/tests/test_uncoded.py +0 -0
@@ -45,9 +45,8 @@ Verify by reading `.uncoded/namespace.yaml` — if it exists and is non-empty,
45
45
  proceed. If not, stop and tell the user to run `uncoded sync` first; the review
46
46
  depends on the index.
47
47
 
48
- If Serena MCP tools are available (`mcp__serena__*`), the structural sweep has
49
- more leverage. The review still works without Serena but will be weaker on
50
- cross-file reference checks.
48
+ The structural sweep uses `uncoded refs` for cross-file reference checks —
49
+ it ships with uncoded itself and is always available when the index is present.
51
50
 
52
51
  ## Workflow
53
52
 
@@ -57,7 +56,7 @@ The review proceeds in four sweeps, each building on the previous:
57
56
  2. **Lexical sweep** — read the namespace, look for naming-level inconsistency.
58
57
  3. **Promissory sweep** — check each symbol's name / signature / docstring
59
58
  for internal disagreement; names and signatures from the stub, docstrings
60
- via `find_symbol(include_body=True)`.
59
+ via `uncoded body`.
61
60
  4. **Structural sweep** — combine namespace and imports to find boundary and
62
61
  shape symptoms.
63
62
 
@@ -97,8 +96,7 @@ model the same entity.
97
96
 
98
97
  Detection: scan the namespace for symbol clusters with verb or noun overlap.
99
98
  Where suspicion arises, check the stub signatures and the source docstrings
100
- (via Serena's `find_symbol` with `include_body=True`) to confirm the
101
- candidates overlap in meaning.
99
+ (via `uncoded body`) to confirm the candidates overlap in meaning.
102
100
 
103
101
  **Qualifier accretion.** Names carrying modifiers that are fossils of
104
102
  iteration: `_new`, `_v2`, `_updated`, `_legacy`, `_real`, `_proper`, `_final`,
@@ -121,17 +119,18 @@ subtly different meanings — visible as different signatures, different docstri
121
119
  content, or different domain associations.
122
120
 
123
121
  Detection: identify name collisions in the namespace, then compare signatures
124
- (from the stubs) and docstrings (via Serena's `find_symbol` with
125
- `include_body=True`) to see whether the uses agree.
122
+ (from the stubs) and docstrings (via `uncoded body`) to see whether the uses
123
+ agree.
126
124
 
127
125
  ## Step 3: Promissory sweep
128
126
 
129
127
  Examine each public symbol's name / signature / docstring triple for internal
130
128
  disagreement. Load each source file's stub once for the names and signatures
131
129
  across that file's symbols. Then, for each non-trivial public symbol (skip
132
- trivial one-liners and `__init__` with no meaningful body), call Serena's
133
- `find_symbol` with `include_body=True` to read the docstring — the call also
134
- returns the body, available if a finding needs it.
130
+ trivial one-liners and `__init__` with no meaningful body), run
131
+ `uncoded body <name_path> --in <relative_path>` to read the symbol's source.
132
+ The docstring is at the top; the rest of the body is available for any
133
+ finding that needs it.
135
134
 
136
135
  **Name–signature mismatch.** Does the name's verb fit the signature's return? A
137
136
  function called `validate_*` that returns the validated object rather than
@@ -153,19 +152,20 @@ this for Y; use Z instead." These are confessions — someone noticed drift and
153
152
  documented it rather than fixing it.
154
153
 
155
154
  Quote evidence verbatim. The stub excerpt is the evidence for name and
156
- signature findings; the docstring returned by `find_symbol` with
157
- `include_body=True` is the evidence for any docstring-related finding.
155
+ signature findings; the docstring read via `uncoded body` is the evidence
156
+ for any docstring-related finding.
158
157
 
159
- **When to read further.** Read the body when a finding's confidence needs it:
160
- a name–behaviour mismatch the docstring alone doesn't settle, or a defensive
158
+ **When confidence needs the body.** Consult the implementation in the body,
159
+ not just the docstring, when a finding's confidence needs it: a
160
+ name–behaviour mismatch the docstring alone doesn't settle, or a defensive
161
161
  docstring you want to verify against the body. Targeted to the symbol, no
162
162
  offset arithmetic, no risk of over-reading. Never read a whole source file
163
163
  during this sweep.
164
164
 
165
165
  ## Step 4: Structural sweep
166
166
 
167
- Combine the namespace with the import graph and, if available, Serena's
168
- reference resolution.
167
+ Combine the namespace with the import graph and `uncoded refs` for
168
+ cross-file reference resolution.
169
169
 
170
170
  **Overgrown public surfaces / god modules.** A module or class whose public
171
171
  namespace is much larger than its siblings, or spans obviously different
@@ -191,8 +191,8 @@ Check systematically, not by spot-check:
191
191
  2. Cross-reference with stub import sections — any symbol imported by another
192
192
  source module is live; remove it from the candidate list. This culls the
193
193
  obvious cases cheaply.
194
- 3. For remaining candidates, use Serena's `find_referencing_symbols` to verify.
195
- If Serena is unavailable, note findings as lower confidence.
194
+ 3. For remaining candidates, use `uncoded refs <name_path> --in <relative_path>`
195
+ to verify. Empty output confirms zero callers.
196
196
  4. Distinguish two sub-cases when reporting:
197
197
  - *No callers anywhere* — dead code; highest priority.
198
198
  - *Callers only in tests* — the symbol is tested but not used in source;
@@ -203,8 +203,7 @@ function in the same module where the function's sole body is `return
203
203
  <constant>`. Both symbols being public exposes an implementation detail
204
204
  unnecessarily — only one needs to be public. Detection: use the stubs to find
205
205
  public parameterless functions near public constants, then verify each
206
- candidate body with Serena's `find_symbol` with `include_body=True` before
207
- reporting.
206
+ candidate body with `uncoded body` before reporting.
208
207
 
209
208
  ## Report format
210
209
 
@@ -245,7 +244,7 @@ Regions with two or more findings — examine these first:
245
244
 
246
245
  **Evidence:**
247
246
  > Verbatim quote from namespace.yaml, stub, source docstring (via
248
- > `find_symbol`), or import statement.
247
+ > `uncoded body`), or import statement.
249
248
 
250
249
  One or two sentences describing the inconsistency. Not a diagnosis. Not a fix.
251
250
 
@@ -45,9 +45,8 @@ Verify by reading `.uncoded/namespace.yaml` — if it exists and is non-empty,
45
45
  proceed. If not, stop and tell the user to run `uncoded sync` first; the review
46
46
  depends on the index.
47
47
 
48
- If Serena MCP tools are available (`mcp__serena__*`), the structural sweep has
49
- more leverage. The review still works without Serena but will be weaker on
50
- cross-file reference checks.
48
+ The structural sweep uses `uncoded refs` for cross-file reference checks —
49
+ it ships with uncoded itself and is always available when the index is present.
51
50
 
52
51
  ## Workflow
53
52
 
@@ -57,7 +56,7 @@ The review proceeds in four sweeps, each building on the previous:
57
56
  2. **Lexical sweep** — read the namespace, look for naming-level inconsistency.
58
57
  3. **Promissory sweep** — check each symbol's name / signature / docstring
59
58
  for internal disagreement; names and signatures from the stub, docstrings
60
- via `find_symbol(include_body=True)`.
59
+ via `uncoded body`.
61
60
  4. **Structural sweep** — combine namespace and imports to find boundary and
62
61
  shape symptoms.
63
62
 
@@ -97,8 +96,7 @@ model the same entity.
97
96
 
98
97
  Detection: scan the namespace for symbol clusters with verb or noun overlap.
99
98
  Where suspicion arises, check the stub signatures and the source docstrings
100
- (via Serena's `find_symbol` with `include_body=True`) to confirm the
101
- candidates overlap in meaning.
99
+ (via `uncoded body`) to confirm the candidates overlap in meaning.
102
100
 
103
101
  **Qualifier accretion.** Names carrying modifiers that are fossils of
104
102
  iteration: `_new`, `_v2`, `_updated`, `_legacy`, `_real`, `_proper`, `_final`,
@@ -121,17 +119,18 @@ subtly different meanings — visible as different signatures, different docstri
121
119
  content, or different domain associations.
122
120
 
123
121
  Detection: identify name collisions in the namespace, then compare signatures
124
- (from the stubs) and docstrings (via Serena's `find_symbol` with
125
- `include_body=True`) to see whether the uses agree.
122
+ (from the stubs) and docstrings (via `uncoded body`) to see whether the uses
123
+ agree.
126
124
 
127
125
  ## Step 3: Promissory sweep
128
126
 
129
127
  Examine each public symbol's name / signature / docstring triple for internal
130
128
  disagreement. Load each source file's stub once for the names and signatures
131
129
  across that file's symbols. Then, for each non-trivial public symbol (skip
132
- trivial one-liners and `__init__` with no meaningful body), call Serena's
133
- `find_symbol` with `include_body=True` to read the docstring — the call also
134
- returns the body, available if a finding needs it.
130
+ trivial one-liners and `__init__` with no meaningful body), run
131
+ `uncoded body <name_path> --in <relative_path>` to read the symbol's source.
132
+ The docstring is at the top; the rest of the body is available for any
133
+ finding that needs it.
135
134
 
136
135
  **Name–signature mismatch.** Does the name's verb fit the signature's return? A
137
136
  function called `validate_*` that returns the validated object rather than
@@ -153,19 +152,20 @@ this for Y; use Z instead." These are confessions — someone noticed drift and
153
152
  documented it rather than fixing it.
154
153
 
155
154
  Quote evidence verbatim. The stub excerpt is the evidence for name and
156
- signature findings; the docstring returned by `find_symbol` with
157
- `include_body=True` is the evidence for any docstring-related finding.
155
+ signature findings; the docstring read via `uncoded body` is the evidence
156
+ for any docstring-related finding.
158
157
 
159
- **When to read further.** Read the body when a finding's confidence needs it:
160
- a name–behaviour mismatch the docstring alone doesn't settle, or a defensive
158
+ **When confidence needs the body.** Consult the implementation in the body,
159
+ not just the docstring, when a finding's confidence needs it: a
160
+ name–behaviour mismatch the docstring alone doesn't settle, or a defensive
161
161
  docstring you want to verify against the body. Targeted to the symbol, no
162
162
  offset arithmetic, no risk of over-reading. Never read a whole source file
163
163
  during this sweep.
164
164
 
165
165
  ## Step 4: Structural sweep
166
166
 
167
- Combine the namespace with the import graph and, if available, Serena's
168
- reference resolution.
167
+ Combine the namespace with the import graph and `uncoded refs` for
168
+ cross-file reference resolution.
169
169
 
170
170
  **Overgrown public surfaces / god modules.** A module or class whose public
171
171
  namespace is much larger than its siblings, or spans obviously different
@@ -191,8 +191,8 @@ Check systematically, not by spot-check:
191
191
  2. Cross-reference with stub import sections — any symbol imported by another
192
192
  source module is live; remove it from the candidate list. This culls the
193
193
  obvious cases cheaply.
194
- 3. For remaining candidates, use Serena's `find_referencing_symbols` to verify.
195
- If Serena is unavailable, note findings as lower confidence.
194
+ 3. For remaining candidates, use `uncoded refs <name_path> --in <relative_path>`
195
+ to verify. Empty output confirms zero callers.
196
196
  4. Distinguish two sub-cases when reporting:
197
197
  - *No callers anywhere* — dead code; highest priority.
198
198
  - *Callers only in tests* — the symbol is tested but not used in source;
@@ -203,8 +203,7 @@ function in the same module where the function's sole body is `return
203
203
  <constant>`. Both symbols being public exposes an implementation detail
204
204
  unnecessarily — only one needs to be public. Detection: use the stubs to find
205
205
  public parameterless functions near public constants, then verify each
206
- candidate body with Serena's `find_symbol` with `include_body=True` before
207
- reporting.
206
+ candidate body with `uncoded body` before reporting.
208
207
 
209
208
  ## Report format
210
209
 
@@ -245,7 +244,7 @@ Regions with two or more findings — examine these first:
245
244
 
246
245
  **Evidence:**
247
246
  > Verbatim quote from namespace.yaml, stub, source docstring (via
248
- > `find_symbol`), or import statement.
247
+ > `uncoded body`), or import statement.
249
248
 
250
249
  One or two sentences describing the inconsistency. Not a diagnosis. Not a fix.
251
250
 
@@ -30,4 +30,4 @@ jobs:
30
30
  - uses: astral-sh/setup-uv@v6
31
31
  - run: uv python install ${{ matrix.python-version }}
32
32
  - run: uv sync --extra dev
33
- - run: uv run pytest -v
33
+ - run: uv run pytest -v -m "integration or not integration"
@@ -205,8 +205,3 @@ cython_debug/
205
205
  marimo/_static/
206
206
  marimo/_lsp/
207
207
  __marimo__/
208
-
209
- # Serena — keep project.yml under version control, ignore runtime artefacts
210
- # (memories, caches, per-user overrides).
211
- .serena/*
212
- !.serena/project.yml
@@ -3,11 +3,6 @@ repos:
3
3
  rev: v5.0.0
4
4
  hooks:
5
5
  - id: trailing-whitespace
6
- # Serena rewrites .serena/project.yml on MCP server start with its
7
- # own default-annotated form, which has a trailing space in one
8
- # comment line. Trimming it here would just trigger a diff on the
9
- # next session — Serena owns this file's formatting, so exclude it.
10
- exclude: ^\.serena/project\.yml$
11
6
  - id: end-of-file-fixer
12
7
  - id: check-yaml
13
8
  - id: check-toml
@@ -11,12 +11,20 @@ src/:
11
11
  uncoded/:
12
12
  __init__.py:
13
13
  __version__:
14
+ ast_helpers.py:
15
+ property_kind:
16
+ assign_target_name:
17
+ body.py:
18
+ resolve_body:
19
+ _extract_body:
14
20
  cli.py:
21
+ _find_project_root:
15
22
  _sync:
23
+ _body:
24
+ _refs:
16
25
  main:
17
26
  config.py:
18
27
  find_pyproject_toml:
19
- read_project_name:
20
28
  read_source_roots:
21
29
  read_instruction_files:
22
30
  extract.py:
@@ -29,8 +37,6 @@ src/:
29
37
  constants:
30
38
  classes:
31
39
  functions:
32
- property_kind:
33
- _assign_target_name:
34
40
  extract_module:
35
41
  iter_source_files:
36
42
  extract_modules:
@@ -48,17 +54,38 @@ src/:
48
54
  increase_indent:
49
55
  build_map:
50
56
  render_map:
51
- serena_setup.py:
52
- _Status:
53
- SERENA_VERSION:
54
- MCP_SERVER_SERENA:
55
- SERENA_PROJECT_FIELDS:
56
- SERENA_ALLOWED_TOOLS:
57
- _STATUS_VERB:
58
- _sync_mcp_json:
59
- _write_serena_project_if_absent:
60
- _sync_claude_settings:
61
- setup:
57
+ refs.py:
58
+ TY_VERSION:
59
+ Reference:
60
+ rel_path:
61
+ line:
62
+ col:
63
+ _LSPLocation:
64
+ path:
65
+ line:
66
+ character:
67
+ find_refs:
68
+ _query_references:
69
+ _find_root:
70
+ _terminate:
71
+ _to_rel_path:
72
+ _run_exchange:
73
+ _write_message:
74
+ _read_message:
75
+ _read_response:
76
+ _uri_to_path:
77
+ resolver.py:
78
+ NamePath:
79
+ head:
80
+ tail:
81
+ parse:
82
+ __str__:
83
+ SymbolNotFound:
84
+ UnsupportedNamePath:
85
+ resolve_ast_node:
86
+ resolve_name_position:
87
+ resolve_ast_node_from_source:
88
+ _resolve_class_member:
62
89
  skill.py:
63
90
  SKILL_OUTPUTS:
64
91
  LEGACY_SKILL_OUTPUTS:
@@ -78,7 +105,7 @@ src/:
78
105
  name:
79
106
  annotation:
80
107
  value_source:
81
- is_type_alias:
108
+ is_pep695_alias:
82
109
  StubClass:
83
110
  name:
84
111
  bases:
@@ -109,11 +136,63 @@ src/:
109
136
  sync_file:
110
137
  remove_file:
111
138
  tests/:
139
+ test_body.py:
140
+ TestResolveBodyTopLevel:
141
+ test_function_without_decorators:
142
+ test_function_with_decorators:
143
+ test_async_function:
144
+ test_class_whole_body:
145
+ test_module_constant_annotated:
146
+ test_module_constant_unannotated:
147
+ test_pep695_type_alias:
148
+ test_overload_returns_last_definition:
149
+ test_scans_past_non_matching_constant:
150
+ test_not_found_raises_body_not_found:
151
+ test_file_not_found_propagates:
152
+ test_syntax_error_propagates:
153
+ TestResolveBodyClassMember:
154
+ test_method:
155
+ test_property_returns_getter_body:
156
+ test_class_attribute_annotated:
157
+ test_class_attribute_unannotated:
158
+ test_scans_past_non_matching_attribute:
159
+ test_skips_non_function_nodes_in_class:
160
+ test_shadowed_class_member_found_in_last_definition:
161
+ test_shadowed_class_member_not_found_in_last_definition:
162
+ test_not_found_in_class:
163
+ TestUnsupportedNamePath:
164
+ SUPPORTED_SHAPES:
165
+ _assert_raises:
166
+ test_three_segment_path:
167
+ test_nested_class_shape:
168
+ test_empty_leading_segment:
169
+ test_empty_trailing_segment:
170
+ test_empty_middle_segment:
171
+ TestResolveAstNode:
172
+ test_returns_function_def_for_top_level_function:
173
+ test_returns_method_node_for_class_member:
174
+ test_raises_body_not_found:
175
+ test_raises_unsupported_name_path:
176
+ test_file_not_found_propagates:
177
+ test_syntax_error_propagates:
178
+ TestResolveNamePosition:
179
+ test_function:
180
+ test_function_decorator_does_not_shift_line:
181
+ test_async_function:
182
+ test_class:
183
+ test_annotated_assignment:
184
+ test_unannotated_assignment:
185
+ test_type_alias:
186
+ test_class_method:
187
+ test_unexpected_node_type_raises_unsupported_name_path:
188
+ TestResolveBodyByteIdentical:
189
+ test_exact_source_returned:
112
190
  test_cli.py:
113
191
  TestSyncApplyMode:
114
192
  test_writes_namespace_map_stubs_and_instruction_file:
115
193
  test_idempotent_second_run:
116
194
  test_dedupes_when_claude_md_is_symlink_to_agents_md:
195
+ test_instruction_file_outside_project_uses_absolute_path:
117
196
  test_error_when_no_pyproject_toml:
118
197
  test_error_when_source_root_missing:
119
198
  test_error_when_uncoded_section_missing:
@@ -132,20 +211,38 @@ tests/:
132
211
  test_sync_subcommand_runs_in_apply_mode:
133
212
  test_check_subcommand_runs_in_check_mode:
134
213
  test_check_subcommand_returns_zero_on_fresh_index:
135
- test_setup_subcommand:
136
214
  test_no_subcommand_is_an_error:
137
215
  test_unknown_subcommand_is_an_error:
216
+ TestBodyCommand:
217
+ test_happy_path_dispatch:
218
+ test_class_method_form:
219
+ test_symbol_not_found_exits_one:
220
+ test_file_not_found_exits_one:
221
+ test_syntax_error_exits_one:
222
+ test_works_without_project_root:
223
+ test_in_path_resolves_relative_to_cwd:
224
+ test_stdout_is_exact_body:
225
+ test_unsupported_name_path_exits_one:
226
+ test_missing_in_flag_exits_with_two:
227
+ TestRefsCommand:
228
+ test_happy_path_dispatch:
229
+ test_class_method_form:
230
+ test_symbol_not_found_exits_one:
231
+ test_file_not_found_exits_one:
232
+ test_syntax_error_exits_one:
233
+ test_works_without_project_root:
234
+ test_in_path_resolves_relative_to_cwd:
235
+ test_unsupported_name_path_exits_one:
236
+ test_missing_in_flag_exits_with_two:
237
+ test_zero_references_exits_zero_with_empty_stdout:
238
+ test_multiple_references_prints_sorted:
239
+ test_lsp_failure_exits_one:
138
240
  _init_repo:
139
241
  test_config.py:
140
242
  TestFindPyprojectToml:
141
243
  test_finds_at_start:
142
244
  test_finds_in_parent_of_start:
143
245
  test_returns_none_if_not_found:
144
- TestReadProjectName:
145
- test_reads_name_from_pyproject_toml:
146
- test_falls_back_to_start_name_when_no_pyproject:
147
- test_falls_back_to_start_name_when_no_project_section:
148
- test_falls_back_to_start_name_when_name_missing:
149
246
  TestReadSourceRoots:
150
247
  test_reads_source_roots:
151
248
  test_raises_if_no_uncoded_section:
@@ -155,7 +252,7 @@ tests/:
155
252
  test_reads_configured_list:
156
253
  test_empty_list_is_respected:
157
254
  test_extract.py:
158
- TestExtractModule:
255
+ TestExtractModuleFromSource:
159
256
  test_classes_and_functions:
160
257
  test_async_functions_and_methods:
161
258
  test_empty_module:
@@ -163,10 +260,13 @@ tests/:
163
260
  test_type_alias_classic:
164
261
  test_type_alias_pep695:
165
262
  test_tuple_unpacking_skipped:
263
+ test_chained_assignment_skipped:
166
264
  test_unannotated_class_variable:
265
+ test_class_tuple_unpacking_skipped:
167
266
  test_annotated_attributes:
168
267
  test_property_classified_as_attribute:
169
268
  test_property_setter_and_deleter_suppressed:
269
+ test_non_property_decorator_classified_as_method:
170
270
  test_preserves_source_order:
171
271
  TestIterAndExtract:
172
272
  test_basic_walk:
@@ -174,7 +274,7 @@ tests/:
174
274
  test_includes_init_with_symbols:
175
275
  test_skips_empty_init:
176
276
  test_skips_syntax_errors:
177
- TestExtractModules:
277
+ TestExtractModulesFromFiles:
178
278
  test_returns_module_info_per_parseable_file:
179
279
  test_preserves_source_order:
180
280
  test_module_with_only_constants_is_kept:
@@ -215,29 +315,38 @@ tests/:
215
315
  test_header_appears_at_top:
216
316
  test_header_mentions_stub_pointer:
217
317
  test_header_does_not_break_yaml_parse:
218
- test_serena_setup.py:
219
- REPO_ROOT:
220
- EXPECTED_EXCLUDED_TOOLS:
221
- EXPECTED_MCP_ARGS:
222
- TestSetup:
223
- _run:
224
- test_creates_all_three_files:
225
- test_mcp_json_is_valid_and_pins_version:
226
- test_serena_project_yml_uses_ty_and_ignores_uncoded:
227
- test_serena_project_yml_escapes_yaml_special_chars_in_name:
228
- test_claude_settings_enables_serena_and_allowlists_tools:
229
- test_idempotent:
230
- test_merges_into_existing_mcp_json:
231
- test_refreshes_stale_serena_entry_in_mcp_json:
232
- test_merges_into_existing_claude_settings:
233
- test_does_not_overwrite_existing_serena_project_yml:
234
- test_does_not_duplicate_on_second_merge:
235
- test_setup_uses_root_name_when_no_pyproject:
236
- test_setup_reads_name_from_pyproject:
237
- TestRepoDogfooding:
238
- test_repo_mcp_json_matches_template_contract:
239
- test_repo_claude_settings_allowlists_every_serena_tool:
240
- test_repo_serena_project_yml_matches_template_contract:
318
+ test_refs.py:
319
+ TestFindRefs:
320
+ test_returns_empty_for_dead_symbol:
321
+ test_finds_multiple_references_across_files:
322
+ test_class_method_shape:
323
+ test_results_are_sorted:
324
+ test_line_and_col_are_one_indexed:
325
+ test_path_with_spaces_is_not_percent_encoded:
326
+ TestToRelPath:
327
+ test_returns_relative_path_when_under_cwd:
328
+ test_returns_absolute_path_when_outside_cwd:
329
+ TestQueryReferences:
330
+ test_finds_call_sites:
331
+ test_uvx_not_found_raises_runtime_error:
332
+ test_returns_empty_list_when_no_references:
333
+ test_returns_lsp_locations_when_popen_succeeds:
334
+ TestRunExchange:
335
+ test_lsp_error_raises:
336
+ test_empty_result_list_returns_empty:
337
+ TestFindRoot:
338
+ test_returns_pyproject_parent_when_found:
339
+ test_returns_in_path_parent_when_not_found:
340
+ TestTerminate:
341
+ test_kills_on_timeout:
342
+ TestReadMessage:
343
+ test_raises_on_closed_stream:
344
+ test_parses_framed_message:
345
+ TestReadResponse:
346
+ test_skips_notifications_until_matching_id:
347
+ _lsp_stream:
348
+ _init_response:
349
+ _shutdown_response:
241
350
  test_skill.py:
242
351
  TestSyncSkill:
243
352
  test_skill_name_and_output_paths:
@@ -263,7 +372,7 @@ tests/:
263
372
  test_class_with_attributes_and_methods:
264
373
  test_class_with_bases:
265
374
  test_class_no_bases:
266
- test_kwargs_and_varargs:
375
+ test_extract_params_covers_input_kind:
267
376
  test_imports_collected:
268
377
  test_syntax_error_raises:
269
378
  test_source_order_preserved:
@@ -275,6 +384,8 @@ tests/:
275
384
  test_type_alias_classic:
276
385
  test_type_alias_pep695:
277
386
  test_tuple_unpacking_skipped:
387
+ test_ann_assign_non_name_target_skipped:
388
+ test_class_tuple_unpacking_skipped:
278
389
  test_class_with_unannotated_attribute:
279
390
  test_property_rendered_as_attribute:
280
391
  test_property_without_return_annotation:
@@ -284,9 +395,9 @@ tests/:
284
395
  test_imports_rendered:
285
396
  test_rendered_stub_has_no_line_range_comments:
286
397
  test_async_function_prefix:
287
- test_function_with_annotations:
398
+ test_render_param_covers_input_kind:
399
+ test_return_annotation_rendered:
288
400
  test_class_with_bases:
289
- test_class_no_bases:
290
401
  test_class_with_no_members_renders_body:
291
402
  test_attribute_with_annotation:
292
403
  test_method_indented:
@@ -298,9 +409,8 @@ tests/:
298
409
  test_constant_bare_annotation_rendered:
299
410
  test_type_alias_pep695_rendered:
300
411
  test_unannotated_class_attribute_rendered:
412
+ test_renders_valid_python_for_representative_source:
301
413
  TestBuildStubs:
302
- _setup:
303
- _build:
304
414
  test_writes_expected_stubs:
305
415
  test_removes_orphan_stub_when_source_deleted:
306
416
  test_removes_orphan_stub_when_source_renamed:
@@ -309,9 +419,8 @@ tests/:
309
419
  test_no_op_when_clean:
310
420
  test_reports_count_on_first_build:
311
421
  test_reports_zero_when_clean:
422
+ test_skips_module_with_no_symbols:
312
423
  TestBuildStubsCheckMode:
313
- _setup:
314
- _build:
315
424
  test_does_not_write_stub_in_check_mode:
316
425
  test_zero_changes_when_clean:
317
426
  test_detects_stale_stub_content:
@@ -322,6 +431,9 @@ tests/:
322
431
  test_prunes_orphan_stubs:
323
432
  test_project_root_anchors_writes_independent_of_cwd:
324
433
  test_project_root_anchors_orphan_pruning_independent_of_cwd:
434
+ test_source_root_outside_project_root_skips_cleanup:
435
+ _setup:
436
+ _build:
325
437
  test_sync.py:
326
438
  TestSyncFile:
327
439
  test_creates_missing_file:
@@ -0,0 +1,9 @@
1
+ # src/uncoded/ast_helpers.py
2
+
3
+ import ast
4
+
5
+ def property_kind(node: ast.FunctionDef | ast.AsyncFunctionDef) -> str | None:
6
+ ...
7
+
8
+ def assign_target_name(node: ast.Assign | ast.AnnAssign) -> str | None:
9
+ ...
@@ -0,0 +1,11 @@
1
+ # src/uncoded/body.py
2
+
3
+ import ast
4
+ from pathlib import Path
5
+ from uncoded.resolver import NamePath, resolve_ast_node_from_source
6
+
7
+ def resolve_body(name_path: NamePath, in_path: Path) -> str:
8
+ ...
9
+
10
+ def _extract_body(*, node: ast.stmt, lines: list[str]) -> str:
11
+ ...
@@ -3,17 +3,28 @@
3
3
  import argparse
4
4
  import sys
5
5
  from pathlib import Path
6
+ from uncoded.body import resolve_body
6
7
  from uncoded.config import find_pyproject_toml, read_instruction_files, read_source_roots
7
8
  from uncoded.extract import extract_modules, iter_source_files
8
9
  from uncoded.instruction_files import sync_instruction_file
9
10
  from uncoded.namespace_map import build_map, render_map
10
- from uncoded.serena_setup import setup
11
+ from uncoded.refs import find_refs
12
+ from uncoded.resolver import NamePath, SymbolNotFound, UnsupportedNamePath
11
13
  from uncoded.skill import sync_skill
12
14
  from uncoded.stubs import build_stubs
13
15
  from uncoded.sync import sync_file
14
16
 
17
+ def _find_project_root(*, start: Path) -> Path | None:
18
+ ...
19
+
15
20
  def _sync(*, start: Path | None, check: bool) -> int:
16
21
  ...
17
22
 
23
+ def _body(*, name_path: str, in_path: str) -> int:
24
+ ...
25
+
26
+ def _refs(*, name_path: str, in_path: str) -> int:
27
+ ...
28
+
18
29
  def main() -> int:
19
30
  ...
@@ -7,9 +7,6 @@ from uncoded.instruction_files import DEFAULT_INSTRUCTION_FILES
7
7
  def find_pyproject_toml(start: Path) -> Path | None:
8
8
  ...
9
9
 
10
- def read_project_name(start: Path) -> str:
11
- ...
12
-
13
10
  def read_source_roots(pyproject_path: Path) -> list[Path]:
14
11
  ...
15
12