uncoded 0.6.0__tar.gz → 0.7.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 → uncoded-0.7.0}/.agents/skills/coherence-review/SKILL.md +31 -30
- {uncoded-0.6.0 → uncoded-0.7.0}/.claude/skills/coherence-review/SKILL.md +31 -30
- {uncoded-0.6.0 → uncoded-0.7.0}/.serena/project.yml +1 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/namespace.yaml +57 -34
- uncoded-0.7.0/.uncoded/stubs/src/uncoded/cli.pyi +19 -0
- uncoded-0.7.0/.uncoded/stubs/src/uncoded/config.pyi +17 -0
- uncoded-0.7.0/.uncoded/stubs/src/uncoded/extract.pyi +33 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/src/uncoded/instruction_files.pyi +1 -7
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/src/uncoded/namespace_map.pyi +0 -5
- uncoded-0.7.0/.uncoded/stubs/src/uncoded/serena_setup.pyi +26 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/src/uncoded/skill.pyi +1 -2
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/src/uncoded/stubs.pyi +7 -35
- uncoded-0.7.0/.uncoded/stubs/src/uncoded/sync.pyi +9 -0
- uncoded-0.7.0/.uncoded/stubs/tests/test_cli.pyi +80 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_config.pyi +15 -9
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_extract.pyi +16 -8
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_instruction_files.pyi +10 -5
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_namespace_map.pyi +0 -8
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_serena_setup.pyi +11 -22
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_skill.pyi +7 -2
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_stubs.pyi +28 -17
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_sync.pyi +15 -2
- {uncoded-0.6.0 → uncoded-0.7.0}/AGENTS.md +29 -14
- uncoded-0.7.0/CLAUDE.md +1 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/PKG-INFO +53 -12
- {uncoded-0.6.0 → uncoded-0.7.0}/README.md +51 -10
- {uncoded-0.6.0 → uncoded-0.7.0}/pyproject.toml +1 -1
- uncoded-0.7.0/src/uncoded/cli.py +177 -0
- uncoded-0.7.0/src/uncoded/config.py +87 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/extract.py +34 -26
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/instruction_files.py +35 -23
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/namespace_map.py +1 -2
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/serena_setup.py +64 -51
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/skill.py +46 -34
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/stubs.py +106 -63
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/sync.py +32 -9
- uncoded-0.7.0/tests/test_cli.py +406 -0
- uncoded-0.7.0/tests/test_config.py +92 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_extract.py +59 -19
- uncoded-0.7.0/tests/test_instruction_files.py +122 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_namespace_map.py +0 -31
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_serena_setup.py +71 -42
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_skill.py +46 -24
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_stubs.py +159 -58
- uncoded-0.7.0/tests/test_sync.py +139 -0
- uncoded-0.6.0/.uncoded/stubs/src/uncoded/cli.pyi +0 -23
- uncoded-0.6.0/.uncoded/stubs/src/uncoded/config.pyi +0 -17
- uncoded-0.6.0/.uncoded/stubs/src/uncoded/extract.pyi +0 -41
- uncoded-0.6.0/.uncoded/stubs/src/uncoded/serena_setup.pyi +0 -32
- uncoded-0.6.0/.uncoded/stubs/src/uncoded/sync.pyi +0 -11
- uncoded-0.6.0/.uncoded/stubs/tests/test_cli.pyi +0 -66
- uncoded-0.6.0/CLAUDE.md +0 -155
- uncoded-0.6.0/MAINTENANCE.md +0 -26
- uncoded-0.6.0/SYSTEM.md +0 -53
- uncoded-0.6.0/src/uncoded/cli.py +0 -103
- uncoded-0.6.0/src/uncoded/config.py +0 -62
- uncoded-0.6.0/tests/test_cli.py +0 -199
- uncoded-0.6.0/tests/test_config.py +0 -83
- uncoded-0.6.0/tests/test_instruction_files.py +0 -92
- uncoded-0.6.0/tests/test_sync.py +0 -82
- {uncoded-0.6.0 → uncoded-0.7.0}/.claude/settings.json +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.github/workflows/ci.yml +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.github/workflows/publish.yml +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.gitignore +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.mcp.json +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.pre-commit-config.yaml +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/reviews/2026-04-25-001215.md +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/src/uncoded/__init__.pyi +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/.uncoded/stubs/tests/test_uncoded.pyi +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/LICENSE +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/resources/lsp-research.md +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/src/uncoded/__init__.py +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/__init__.py +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/tests/test_uncoded.py +0 -0
- {uncoded-0.6.0 → uncoded-0.7.0}/uv.lock +0 -0
|
@@ -55,8 +55,9 @@ The review proceeds in four sweeps, each building on the previous:
|
|
|
55
55
|
|
|
56
56
|
1. **Orient** — load the navigation index and form a mental map.
|
|
57
57
|
2. **Lexical sweep** — read the namespace, look for naming-level inconsistency.
|
|
58
|
-
3. **Promissory sweep** —
|
|
59
|
-
|
|
58
|
+
3. **Promissory sweep** — check each symbol's name / signature / docstring
|
|
59
|
+
for internal disagreement; names and signatures from the stub, docstrings
|
|
60
|
+
via `find_symbol(include_body=True)`.
|
|
60
61
|
4. **Structural sweep** — combine namespace and imports to find boundary and
|
|
61
62
|
shape symptoms.
|
|
62
63
|
|
|
@@ -66,9 +67,8 @@ until the end.
|
|
|
66
67
|
## Step 1: Orient
|
|
67
68
|
|
|
68
69
|
Read `.uncoded/namespace.yaml` in full. This is the map — directories, files,
|
|
69
|
-
classes, methods, functions
|
|
70
|
-
|
|
71
|
-
evidence.
|
|
70
|
+
classes, methods, functions. Do not skim. Every public symbol in the codebase
|
|
71
|
+
is listed here, and the shape of the namespace itself is evidence.
|
|
72
72
|
|
|
73
73
|
While reading, note:
|
|
74
74
|
|
|
@@ -96,8 +96,9 @@ interchangeably. Two classes called `UserRecord` and `AccountProfile` that
|
|
|
96
96
|
model the same entity.
|
|
97
97
|
|
|
98
98
|
Detection: scan the namespace for symbol clusters with verb or noun overlap.
|
|
99
|
-
Where suspicion arises,
|
|
100
|
-
|
|
99
|
+
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.
|
|
101
102
|
|
|
102
103
|
**Qualifier accretion.** Names carrying modifiers that are fossils of
|
|
103
104
|
iteration: `_new`, `_v2`, `_updated`, `_legacy`, `_real`, `_proper`, `_final`,
|
|
@@ -119,16 +120,18 @@ with the rest of the codebase.
|
|
|
119
120
|
subtly different meanings — visible as different signatures, different docstring
|
|
120
121
|
content, or different domain associations.
|
|
121
122
|
|
|
122
|
-
Detection: identify name collisions in the namespace, then
|
|
123
|
-
|
|
123
|
+
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.
|
|
124
126
|
|
|
125
127
|
## Step 3: Promissory sweep
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
Examine each public symbol's name / signature / docstring triple for internal
|
|
130
|
+
disagreement. Load each source file's stub once for the names and signatures
|
|
131
|
+
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.
|
|
132
135
|
|
|
133
136
|
**Name–signature mismatch.** Does the name's verb fit the signature's return? A
|
|
134
137
|
function called `validate_*` that returns the validated object rather than
|
|
@@ -149,18 +152,15 @@ describe it. "Note: this does not actually X despite the name." "Do not use
|
|
|
149
152
|
this for Y; use Z instead." These are confessions — someone noticed drift and
|
|
150
153
|
documented it rather than fixing it.
|
|
151
154
|
|
|
152
|
-
The stub
|
|
153
|
-
|
|
155
|
+
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.
|
|
154
158
|
|
|
155
|
-
**When to read
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
the stub alone doesn't confirm it; a defensive docstring you want to verify is
|
|
161
|
-
accurate. Use Serena's `find_symbol` with `include_body=True` — targeted to the
|
|
162
|
-
symbol, no offset arithmetic, no risk of over-reading. Never read a whole source
|
|
163
|
-
file during this sweep.
|
|
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
|
|
161
|
+
docstring you want to verify against the body. Targeted to the symbol, no
|
|
162
|
+
offset arithmetic, no risk of over-reading. Never read a whole source file
|
|
163
|
+
during this sweep.
|
|
164
164
|
|
|
165
165
|
## Step 4: Structural sweep
|
|
166
166
|
|
|
@@ -244,7 +244,8 @@ Regions with two or more findings — examine these first:
|
|
|
244
244
|
**Confidence:** high | medium | low
|
|
245
245
|
|
|
246
246
|
**Evidence:**
|
|
247
|
-
> Verbatim quote from namespace.yaml, stub,
|
|
247
|
+
> Verbatim quote from namespace.yaml, stub, source docstring (via
|
|
248
|
+
> `find_symbol`), or import statement.
|
|
248
249
|
|
|
249
250
|
One or two sentences describing the inconsistency. Not a diagnosis. Not a fix.
|
|
250
251
|
|
|
@@ -261,14 +262,14 @@ clear evidence is useful — the human can filter. A dropped finding is not.
|
|
|
261
262
|
|
|
262
263
|
**Confidence is part of the finding, not a gate.**
|
|
263
264
|
|
|
264
|
-
- `high` — the inconsistency is explicit; evidence is directly in the
|
|
265
|
-
namespace
|
|
265
|
+
- `high` — the inconsistency is explicit; evidence is directly in the
|
|
266
|
+
namespace, the stub, or the source docstring
|
|
266
267
|
- `medium` — strongly implied but depends on judgement about intent
|
|
267
268
|
- `low` — pattern-based suspicion that needs human interpretation
|
|
268
269
|
|
|
269
270
|
**Evidence must be verbatim.** Quote the relevant namespace line, stub excerpt,
|
|
270
|
-
or import statement exactly. A finding the human cannot
|
|
271
|
-
than no finding.
|
|
271
|
+
source docstring, or import statement exactly. A finding the human cannot
|
|
272
|
+
quickly verify is worse than no finding.
|
|
272
273
|
|
|
273
274
|
**One finding per inconsistency.** If a single symbol has a name–signature
|
|
274
275
|
mismatch and a docstring–name mismatch, that is two findings on the same
|
|
@@ -55,8 +55,9 @@ The review proceeds in four sweeps, each building on the previous:
|
|
|
55
55
|
|
|
56
56
|
1. **Orient** — load the navigation index and form a mental map.
|
|
57
57
|
2. **Lexical sweep** — read the namespace, look for naming-level inconsistency.
|
|
58
|
-
3. **Promissory sweep** —
|
|
59
|
-
|
|
58
|
+
3. **Promissory sweep** — check each symbol's name / signature / docstring
|
|
59
|
+
for internal disagreement; names and signatures from the stub, docstrings
|
|
60
|
+
via `find_symbol(include_body=True)`.
|
|
60
61
|
4. **Structural sweep** — combine namespace and imports to find boundary and
|
|
61
62
|
shape symptoms.
|
|
62
63
|
|
|
@@ -66,9 +67,8 @@ until the end.
|
|
|
66
67
|
## Step 1: Orient
|
|
67
68
|
|
|
68
69
|
Read `.uncoded/namespace.yaml` in full. This is the map — directories, files,
|
|
69
|
-
classes, methods, functions
|
|
70
|
-
|
|
71
|
-
evidence.
|
|
70
|
+
classes, methods, functions. Do not skim. Every public symbol in the codebase
|
|
71
|
+
is listed here, and the shape of the namespace itself is evidence.
|
|
72
72
|
|
|
73
73
|
While reading, note:
|
|
74
74
|
|
|
@@ -96,8 +96,9 @@ interchangeably. Two classes called `UserRecord` and `AccountProfile` that
|
|
|
96
96
|
model the same entity.
|
|
97
97
|
|
|
98
98
|
Detection: scan the namespace for symbol clusters with verb or noun overlap.
|
|
99
|
-
Where suspicion arises,
|
|
100
|
-
|
|
99
|
+
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.
|
|
101
102
|
|
|
102
103
|
**Qualifier accretion.** Names carrying modifiers that are fossils of
|
|
103
104
|
iteration: `_new`, `_v2`, `_updated`, `_legacy`, `_real`, `_proper`, `_final`,
|
|
@@ -119,16 +120,18 @@ with the rest of the codebase.
|
|
|
119
120
|
subtly different meanings — visible as different signatures, different docstring
|
|
120
121
|
content, or different domain associations.
|
|
121
122
|
|
|
122
|
-
Detection: identify name collisions in the namespace, then
|
|
123
|
-
|
|
123
|
+
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.
|
|
124
126
|
|
|
125
127
|
## Step 3: Promissory sweep
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
Examine each public symbol's name / signature / docstring triple for internal
|
|
130
|
+
disagreement. Load each source file's stub once for the names and signatures
|
|
131
|
+
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.
|
|
132
135
|
|
|
133
136
|
**Name–signature mismatch.** Does the name's verb fit the signature's return? A
|
|
134
137
|
function called `validate_*` that returns the validated object rather than
|
|
@@ -149,18 +152,15 @@ describe it. "Note: this does not actually X despite the name." "Do not use
|
|
|
149
152
|
this for Y; use Z instead." These are confessions — someone noticed drift and
|
|
150
153
|
documented it rather than fixing it.
|
|
151
154
|
|
|
152
|
-
The stub
|
|
153
|
-
|
|
155
|
+
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.
|
|
154
158
|
|
|
155
|
-
**When to read
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
the stub alone doesn't confirm it; a defensive docstring you want to verify is
|
|
161
|
-
accurate. Use Serena's `find_symbol` with `include_body=True` — targeted to the
|
|
162
|
-
symbol, no offset arithmetic, no risk of over-reading. Never read a whole source
|
|
163
|
-
file during this sweep.
|
|
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
|
|
161
|
+
docstring you want to verify against the body. Targeted to the symbol, no
|
|
162
|
+
offset arithmetic, no risk of over-reading. Never read a whole source file
|
|
163
|
+
during this sweep.
|
|
164
164
|
|
|
165
165
|
## Step 4: Structural sweep
|
|
166
166
|
|
|
@@ -244,7 +244,8 @@ Regions with two or more findings — examine these first:
|
|
|
244
244
|
**Confidence:** high | medium | low
|
|
245
245
|
|
|
246
246
|
**Evidence:**
|
|
247
|
-
> Verbatim quote from namespace.yaml, stub,
|
|
247
|
+
> Verbatim quote from namespace.yaml, stub, source docstring (via
|
|
248
|
+
> `find_symbol`), or import statement.
|
|
248
249
|
|
|
249
250
|
One or two sentences describing the inconsistency. Not a diagnosis. Not a fix.
|
|
250
251
|
|
|
@@ -261,14 +262,14 @@ clear evidence is useful — the human can filter. A dropped finding is not.
|
|
|
261
262
|
|
|
262
263
|
**Confidence is part of the finding, not a gate.**
|
|
263
264
|
|
|
264
|
-
- `high` — the inconsistency is explicit; evidence is directly in the
|
|
265
|
-
namespace
|
|
265
|
+
- `high` — the inconsistency is explicit; evidence is directly in the
|
|
266
|
+
namespace, the stub, or the source docstring
|
|
266
267
|
- `medium` — strongly implied but depends on judgement about intent
|
|
267
268
|
- `low` — pattern-based suspicion that needs human interpretation
|
|
268
269
|
|
|
269
270
|
**Evidence must be verbatim.** Quote the relevant namespace line, stub excerpt,
|
|
270
|
-
or import statement exactly. A finding the human cannot
|
|
271
|
-
than no finding.
|
|
271
|
+
source docstring, or import statement exactly. A finding the human cannot
|
|
272
|
+
quickly verify is worse than no finding.
|
|
272
273
|
|
|
273
274
|
**One finding per inconsistency.** If a single symbol has a name–signature
|
|
274
275
|
mismatch and a docstring–name mismatch, that is two findings on the same
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
# Generated by uncoded — do not edit; regeneration overwrites.
|
|
3
3
|
#
|
|
4
4
|
# Pure key hierarchy (no lists, no values); indent to zoom in.
|
|
5
|
-
# Directory keys end with "/".
|
|
6
|
-
# in source order.
|
|
5
|
+
# Directory keys end with "/".
|
|
7
6
|
#
|
|
8
7
|
# For signatures and types, see stubs:
|
|
9
8
|
# src/foo/bar.py → .uncoded/stubs/src/foo/bar.pyi
|
|
@@ -13,11 +12,11 @@ src/:
|
|
|
13
12
|
__init__.py:
|
|
14
13
|
__version__:
|
|
15
14
|
cli.py:
|
|
16
|
-
DEFAULT_MAP_OUTPUT:
|
|
17
15
|
_sync:
|
|
18
16
|
main:
|
|
19
17
|
config.py:
|
|
20
18
|
find_pyproject_toml:
|
|
19
|
+
read_project_name:
|
|
21
20
|
read_source_roots:
|
|
22
21
|
read_instruction_files:
|
|
23
22
|
extract.py:
|
|
@@ -30,18 +29,17 @@ src/:
|
|
|
30
29
|
constants:
|
|
31
30
|
classes:
|
|
32
31
|
functions:
|
|
33
|
-
|
|
32
|
+
property_kind:
|
|
34
33
|
_assign_target_name:
|
|
35
34
|
extract_module:
|
|
36
35
|
iter_source_files:
|
|
37
|
-
|
|
36
|
+
extract_modules:
|
|
38
37
|
instruction_files.py:
|
|
39
38
|
MARKER_START:
|
|
40
39
|
MARKER_END:
|
|
41
40
|
DEFAULT_INSTRUCTION_FILES:
|
|
42
41
|
_SECTION_BODY:
|
|
43
42
|
SECTION:
|
|
44
|
-
generate_section:
|
|
45
43
|
_replace_or_append:
|
|
46
44
|
sync_instruction_file:
|
|
47
45
|
namespace_map.py:
|
|
@@ -51,16 +49,16 @@ src/:
|
|
|
51
49
|
build_map:
|
|
52
50
|
render_map:
|
|
53
51
|
serena_setup.py:
|
|
52
|
+
_Status:
|
|
54
53
|
SERENA_VERSION:
|
|
55
54
|
MCP_SERVER_SERENA:
|
|
56
|
-
|
|
55
|
+
SERENA_PROJECT_FIELDS:
|
|
57
56
|
SERENA_ALLOWED_TOOLS:
|
|
58
57
|
_STATUS_VERB:
|
|
59
|
-
read_project_name:
|
|
60
58
|
_sync_mcp_json:
|
|
61
|
-
|
|
59
|
+
_write_serena_project_if_absent:
|
|
62
60
|
_sync_claude_settings:
|
|
63
|
-
|
|
61
|
+
setup:
|
|
64
62
|
skill.py:
|
|
65
63
|
SKILL_OUTPUTS:
|
|
66
64
|
LEGACY_SKILL_OUTPUTS:
|
|
@@ -68,7 +66,6 @@ src/:
|
|
|
68
66
|
sync_skill:
|
|
69
67
|
stubs.py:
|
|
70
68
|
VALUE_WIDTH_CAP:
|
|
71
|
-
DEFAULT_STUBS_OUTPUT:
|
|
72
69
|
StubParam:
|
|
73
70
|
name:
|
|
74
71
|
annotation:
|
|
@@ -76,7 +73,6 @@ src/:
|
|
|
76
73
|
name:
|
|
77
74
|
params:
|
|
78
75
|
return_annotation:
|
|
79
|
-
docstring_excerpt:
|
|
80
76
|
is_async:
|
|
81
77
|
StubAssignment:
|
|
82
78
|
name:
|
|
@@ -86,7 +82,6 @@ src/:
|
|
|
86
82
|
StubClass:
|
|
87
83
|
name:
|
|
88
84
|
bases:
|
|
89
|
-
docstring_excerpt:
|
|
90
85
|
attributes:
|
|
91
86
|
methods:
|
|
92
87
|
StubModule:
|
|
@@ -95,7 +90,6 @@ src/:
|
|
|
95
90
|
constants:
|
|
96
91
|
classes:
|
|
97
92
|
functions:
|
|
98
|
-
_first_sentence:
|
|
99
93
|
_extract_params:
|
|
100
94
|
_render_value:
|
|
101
95
|
_extract_assignment:
|
|
@@ -109,6 +103,7 @@ src/:
|
|
|
109
103
|
_render_assignment:
|
|
110
104
|
render_stub:
|
|
111
105
|
_generate_stubs:
|
|
106
|
+
_write_stubs:
|
|
112
107
|
build_stubs:
|
|
113
108
|
sync.py:
|
|
114
109
|
sync_file:
|
|
@@ -118,31 +113,41 @@ tests/:
|
|
|
118
113
|
TestSyncApplyMode:
|
|
119
114
|
test_writes_namespace_map_stubs_and_instruction_file:
|
|
120
115
|
test_idempotent_second_run:
|
|
116
|
+
test_dedupes_when_claude_md_is_symlink_to_agents_md:
|
|
121
117
|
test_error_when_no_pyproject_toml:
|
|
122
118
|
test_error_when_source_root_missing:
|
|
119
|
+
test_error_when_uncoded_section_missing:
|
|
120
|
+
test_skip_warning_emitted_once_per_broken_file:
|
|
121
|
+
test_anchors_reads_and_writes_at_project_root_when_cwd_is_subdir:
|
|
122
|
+
test_artefacts_match_when_run_from_subdir_vs_project_root:
|
|
123
123
|
TestSyncCheckMode:
|
|
124
124
|
test_returns_one_and_does_not_write_on_empty_repo:
|
|
125
125
|
test_returns_zero_when_index_is_up_to_date:
|
|
126
126
|
test_returns_one_when_source_changes_after_sync:
|
|
127
127
|
test_returns_one_when_source_file_deleted:
|
|
128
128
|
test_returns_one_when_instruction_file_drifts:
|
|
129
|
+
test_dedupes_when_claude_md_is_symlink_to_agents_md:
|
|
129
130
|
test_error_still_returns_one:
|
|
130
131
|
TestMainDispatch:
|
|
131
132
|
test_sync_subcommand_runs_in_apply_mode:
|
|
132
133
|
test_check_subcommand_runs_in_check_mode:
|
|
133
134
|
test_check_subcommand_returns_zero_on_fresh_index:
|
|
134
|
-
|
|
135
|
+
test_setup_subcommand:
|
|
135
136
|
test_no_subcommand_is_an_error:
|
|
136
137
|
test_unknown_subcommand_is_an_error:
|
|
137
138
|
_init_repo:
|
|
138
139
|
test_config.py:
|
|
139
140
|
TestFindPyprojectToml:
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
test_finds_at_start:
|
|
142
|
+
test_finds_in_parent_of_start:
|
|
142
143
|
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:
|
|
143
149
|
TestReadSourceRoots:
|
|
144
150
|
test_reads_source_roots:
|
|
145
|
-
test_raises_if_no_pyproject_toml:
|
|
146
151
|
test_raises_if_no_uncoded_section:
|
|
147
152
|
TestReadInstructionFiles:
|
|
148
153
|
test_returns_default_when_no_pyproject_toml:
|
|
@@ -159,19 +164,23 @@ tests/:
|
|
|
159
164
|
test_type_alias_pep695:
|
|
160
165
|
test_tuple_unpacking_skipped:
|
|
161
166
|
test_unannotated_class_variable:
|
|
162
|
-
test_module_with_only_constants_is_kept:
|
|
163
167
|
test_annotated_attributes:
|
|
164
168
|
test_property_classified_as_attribute:
|
|
165
169
|
test_property_setter_and_deleter_suppressed:
|
|
166
170
|
test_preserves_source_order:
|
|
167
|
-
|
|
171
|
+
TestIterAndExtract:
|
|
168
172
|
test_basic_walk:
|
|
169
173
|
test_nested_subpackage:
|
|
170
174
|
test_includes_init_with_symbols:
|
|
171
175
|
test_skips_empty_init:
|
|
172
176
|
test_skips_syntax_errors:
|
|
177
|
+
TestExtractModules:
|
|
178
|
+
test_returns_module_info_per_parseable_file:
|
|
179
|
+
test_preserves_source_order:
|
|
180
|
+
test_module_with_only_constants_is_kept:
|
|
181
|
+
test_skips_files_with_no_symbols:
|
|
173
182
|
test_instruction_files.py:
|
|
174
|
-
|
|
183
|
+
TestSection:
|
|
175
184
|
test_contains_markers:
|
|
176
185
|
test_markers_in_order:
|
|
177
186
|
test_ends_with_newline:
|
|
@@ -187,6 +196,9 @@ tests/:
|
|
|
187
196
|
test_does_not_create_file:
|
|
188
197
|
test_does_not_update_existing_file:
|
|
189
198
|
test_reports_no_change_when_clean:
|
|
199
|
+
TestSyncInstructionFileProjectRootAnchor:
|
|
200
|
+
test_project_root_anchors_create_independent_of_cwd:
|
|
201
|
+
test_project_root_anchors_update_of_existing_file:
|
|
190
202
|
test_namespace_map.py:
|
|
191
203
|
TestBuildMap:
|
|
192
204
|
test_single_file:
|
|
@@ -195,9 +207,7 @@ tests/:
|
|
|
195
207
|
test_class_with_attributes_and_methods:
|
|
196
208
|
test_function_is_none:
|
|
197
209
|
test_class_with_no_members:
|
|
198
|
-
test_source_order_preserved:
|
|
199
210
|
test_module_level_constants:
|
|
200
|
-
test_constants_precede_classes_and_functions:
|
|
201
211
|
TestRenderMap:
|
|
202
212
|
test_roundtrips_through_yaml:
|
|
203
213
|
test_preserves_insertion_order:
|
|
@@ -208,16 +218,13 @@ tests/:
|
|
|
208
218
|
test_serena_setup.py:
|
|
209
219
|
REPO_ROOT:
|
|
210
220
|
EXPECTED_EXCLUDED_TOOLS:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
test_falls_back_to_cwd_name_when_no_pyproject:
|
|
214
|
-
test_falls_back_to_cwd_name_when_no_project_section:
|
|
215
|
-
test_falls_back_to_cwd_name_when_name_missing:
|
|
216
|
-
TestSetupSerena:
|
|
221
|
+
EXPECTED_MCP_ARGS:
|
|
222
|
+
TestSetup:
|
|
217
223
|
_run:
|
|
218
224
|
test_creates_all_three_files:
|
|
219
225
|
test_mcp_json_is_valid_and_pins_version:
|
|
220
226
|
test_serena_project_yml_uses_ty_and_ignores_uncoded:
|
|
227
|
+
test_serena_project_yml_escapes_yaml_special_chars_in_name:
|
|
221
228
|
test_claude_settings_enables_serena_and_allowlists_tools:
|
|
222
229
|
test_idempotent:
|
|
223
230
|
test_merges_into_existing_mcp_json:
|
|
@@ -225,9 +232,10 @@ tests/:
|
|
|
225
232
|
test_merges_into_existing_claude_settings:
|
|
226
233
|
test_does_not_overwrite_existing_serena_project_yml:
|
|
227
234
|
test_does_not_duplicate_on_second_merge:
|
|
228
|
-
|
|
235
|
+
test_setup_uses_root_name_when_no_pyproject:
|
|
236
|
+
test_setup_reads_name_from_pyproject:
|
|
229
237
|
TestRepoDogfooding:
|
|
230
|
-
|
|
238
|
+
test_repo_mcp_json_matches_template_contract:
|
|
231
239
|
test_repo_claude_settings_allowlists_every_serena_tool:
|
|
232
240
|
test_repo_serena_project_yml_matches_template_contract:
|
|
233
241
|
test_skill.py:
|
|
@@ -243,6 +251,9 @@ tests/:
|
|
|
243
251
|
test_check_mode_reports_no_change_when_in_sync:
|
|
244
252
|
test_removes_legacy_skill_files:
|
|
245
253
|
test_check_mode_reports_legacy_skill_files_without_removing:
|
|
254
|
+
TestSyncSkillProjectRootAnchor:
|
|
255
|
+
test_project_root_anchors_writes_independent_of_cwd:
|
|
256
|
+
test_project_root_anchors_legacy_removal_independent_of_cwd:
|
|
246
257
|
test_stubs.py:
|
|
247
258
|
TestExtractStub:
|
|
248
259
|
test_simple_function:
|
|
@@ -252,8 +263,6 @@ tests/:
|
|
|
252
263
|
test_class_with_attributes_and_methods:
|
|
253
264
|
test_class_with_bases:
|
|
254
265
|
test_class_no_bases:
|
|
255
|
-
test_docstring_first_sentence_only:
|
|
256
|
-
test_no_docstring:
|
|
257
266
|
test_kwargs_and_varargs:
|
|
258
267
|
test_imports_collected:
|
|
259
268
|
test_syntax_error_raises:
|
|
@@ -276,9 +285,9 @@ tests/:
|
|
|
276
285
|
test_rendered_stub_has_no_line_range_comments:
|
|
277
286
|
test_async_function_prefix:
|
|
278
287
|
test_function_with_annotations:
|
|
279
|
-
test_docstring_excerpt_rendered:
|
|
280
288
|
test_class_with_bases:
|
|
281
289
|
test_class_no_bases:
|
|
290
|
+
test_class_with_no_members_renders_body:
|
|
282
291
|
test_attribute_with_annotation:
|
|
283
292
|
test_method_indented:
|
|
284
293
|
test_ends_with_newline:
|
|
@@ -291,6 +300,7 @@ tests/:
|
|
|
291
300
|
test_unannotated_class_attribute_rendered:
|
|
292
301
|
TestBuildStubs:
|
|
293
302
|
_setup:
|
|
303
|
+
_build:
|
|
294
304
|
test_writes_expected_stubs:
|
|
295
305
|
test_removes_orphan_stub_when_source_deleted:
|
|
296
306
|
test_removes_orphan_stub_when_source_renamed:
|
|
@@ -301,10 +311,17 @@ tests/:
|
|
|
301
311
|
test_reports_zero_when_clean:
|
|
302
312
|
TestBuildStubsCheckMode:
|
|
303
313
|
_setup:
|
|
314
|
+
_build:
|
|
304
315
|
test_does_not_write_stub_in_check_mode:
|
|
305
316
|
test_zero_changes_when_clean:
|
|
306
317
|
test_detects_stale_stub_content:
|
|
307
318
|
test_detects_orphan_stub_without_removing_it:
|
|
319
|
+
TestWriteStubs:
|
|
320
|
+
test_writes_stubs:
|
|
321
|
+
test_check_mode_does_not_write:
|
|
322
|
+
test_prunes_orphan_stubs:
|
|
323
|
+
test_project_root_anchors_writes_independent_of_cwd:
|
|
324
|
+
test_project_root_anchors_orphan_pruning_independent_of_cwd:
|
|
308
325
|
test_sync.py:
|
|
309
326
|
TestSyncFile:
|
|
310
327
|
test_creates_missing_file:
|
|
@@ -320,6 +337,12 @@ tests/:
|
|
|
320
337
|
test_noop_when_absent:
|
|
321
338
|
test_check_mode_does_not_remove:
|
|
322
339
|
test_check_mode_reports_noop_when_absent:
|
|
340
|
+
TestSyncFileProjectRootAnchor:
|
|
341
|
+
test_project_root_anchors_write_independent_of_cwd:
|
|
342
|
+
test_project_root_preserves_relative_path_in_message:
|
|
343
|
+
test_absolute_path_makes_project_root_a_no_op:
|
|
344
|
+
TestRemoveFileProjectRootAnchor:
|
|
345
|
+
test_project_root_anchors_removal_independent_of_cwd:
|
|
323
346
|
test_uncoded.py:
|
|
324
347
|
test_import:
|
|
325
348
|
test_version_is_exposed:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# src/uncoded/cli.py
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from uncoded.config import find_pyproject_toml, read_instruction_files, read_source_roots
|
|
7
|
+
from uncoded.extract import extract_modules, iter_source_files
|
|
8
|
+
from uncoded.instruction_files import sync_instruction_file
|
|
9
|
+
from uncoded.namespace_map import build_map, render_map
|
|
10
|
+
from uncoded.serena_setup import setup
|
|
11
|
+
from uncoded.skill import sync_skill
|
|
12
|
+
from uncoded.stubs import build_stubs
|
|
13
|
+
from uncoded.sync import sync_file
|
|
14
|
+
|
|
15
|
+
def _sync(*, start: Path | None, check: bool) -> int:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def main() -> int:
|
|
19
|
+
...
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# src/uncoded/config.py
|
|
2
|
+
|
|
3
|
+
import tomllib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from uncoded.instruction_files import DEFAULT_INSTRUCTION_FILES
|
|
6
|
+
|
|
7
|
+
def find_pyproject_toml(start: Path) -> Path | None:
|
|
8
|
+
...
|
|
9
|
+
|
|
10
|
+
def read_project_name(start: Path) -> str:
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
def read_source_roots(pyproject_path: Path) -> list[Path]:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
def read_instruction_files(start: Path) -> list[Path]:
|
|
17
|
+
...
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# src/uncoded/extract.py
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import sys
|
|
5
|
+
from collections.abc import Iterable, Iterator
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
def property_kind(node: ast.FunctionDef | ast.AsyncFunctionDef) -> str | None:
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
def _assign_target_name(node: ast.Assign | ast.AnnAssign) -> str | None:
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
def extract_module(source: str, rel_path: str) -> ModuleInfo:
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
def iter_source_files(source_root: Path, project_root: Path) -> Iterator[tuple[str, str]]:
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def extract_modules(files: Iterable[tuple[str, str]]) -> list[ModuleInfo]:
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
class ClassInfo:
|
|
25
|
+
name: str
|
|
26
|
+
attributes: list[str] = field(default_factory=list)
|
|
27
|
+
methods: list[str] = field(default_factory=list)
|
|
28
|
+
|
|
29
|
+
class ModuleInfo:
|
|
30
|
+
rel_path: str
|
|
31
|
+
constants: list[str] = field(default_factory=list)
|
|
32
|
+
classes: list[ClassInfo] = field(default_factory=list)
|
|
33
|
+
functions: list[str] = field(default_factory=list)
|
|
@@ -9,14 +9,8 @@ DEFAULT_INSTRUCTION_FILES = [Path('CLAUDE.md'), Path('AGENTS.md')]
|
|
|
9
9
|
_SECTION_BODY = ...
|
|
10
10
|
SECTION = f'{MARKER_START}\n{_SECTION_BODY}\n{MARKER_END}\n'
|
|
11
11
|
|
|
12
|
-
def generate_section() -> str:
|
|
13
|
-
"""Return the full delimited uncoded section for an instruction file."""
|
|
14
|
-
...
|
|
15
|
-
|
|
16
12
|
def _replace_or_append(existing: str, section: str) -> str:
|
|
17
|
-
"""Replace the delimited section in existing text, or append it if absent."""
|
|
18
13
|
...
|
|
19
14
|
|
|
20
|
-
def sync_instruction_file(path: Path, *, check: bool) -> bool:
|
|
21
|
-
"""Write or update the uncoded navigation section in an instruction file."""
|
|
15
|
+
def sync_instruction_file(path: Path, *, project_root: Path, check: bool) -> bool:
|
|
22
16
|
...
|
|
@@ -7,16 +7,11 @@ from uncoded.extract import ModuleInfo
|
|
|
7
7
|
HEADER = ...
|
|
8
8
|
|
|
9
9
|
def build_map(modules: list[ModuleInfo]) -> dict:
|
|
10
|
-
"""Build a nested dict representing the namespace."""
|
|
11
10
|
...
|
|
12
11
|
|
|
13
12
|
def render_map(namespace: dict) -> str:
|
|
14
|
-
"""Render a namespace map dict as a YAML string with an explanatory header."""
|
|
15
13
|
...
|
|
16
14
|
|
|
17
15
|
class _CleanDumper(yaml.SafeDumper):
|
|
18
|
-
"""YAML dumper that indents list items and suppresses 'null' values."""
|
|
19
|
-
|
|
20
16
|
def increase_indent(self, flow, indentless):
|
|
21
|
-
"""Force list items to be indented relative to their parent key."""
|
|
22
17
|
...
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# src/uncoded/serena_setup.py
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal
|
|
6
|
+
import yaml
|
|
7
|
+
from uncoded.config import read_project_name
|
|
8
|
+
|
|
9
|
+
type _Status = Literal['wrote', 'updated', 'unchanged']
|
|
10
|
+
SERENA_VERSION = '1.1.2'
|
|
11
|
+
MCP_SERVER_SERENA = ...
|
|
12
|
+
SERENA_PROJECT_FIELDS = ...
|
|
13
|
+
SERENA_ALLOWED_TOOLS = ...
|
|
14
|
+
_STATUS_VERB: dict[_Status, str] = {'wrote': 'Wrote', 'updated': 'Updated', 'unchanged': 'Unchanged'}
|
|
15
|
+
|
|
16
|
+
def _sync_mcp_json(path: Path) -> _Status:
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
def _write_serena_project_if_absent(path: Path, project_name: str) -> _Status:
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
def _sync_claude_settings(path: Path) -> _Status:
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def setup(root: Path | None) -> int:
|
|
26
|
+
...
|