codetruth 0.2.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.
- codetruth-0.2.0/LICENSE +21 -0
- codetruth-0.2.0/PKG-INFO +215 -0
- codetruth-0.2.0/README.md +180 -0
- codetruth-0.2.0/codetruth/__init__.py +16 -0
- codetruth-0.2.0/codetruth/api.py +102 -0
- codetruth-0.2.0/codetruth/cli.py +183 -0
- codetruth-0.2.0/codetruth/core/__init__.py +0 -0
- codetruth-0.2.0/codetruth/core/cache.py +104 -0
- codetruth-0.2.0/codetruth/core/config.py +74 -0
- codetruth-0.2.0/codetruth/core/deletion.py +165 -0
- codetruth-0.2.0/codetruth/core/evidence.py +306 -0
- codetruth-0.2.0/codetruth/core/graph.py +44 -0
- codetruth-0.2.0/codetruth/core/models.py +193 -0
- codetruth-0.2.0/codetruth/core/plugin.py +88 -0
- codetruth-0.2.0/codetruth/core/report.py +107 -0
- codetruth-0.2.0/codetruth/core/scanner.py +228 -0
- codetruth-0.2.0/codetruth/languages/__init__.py +0 -0
- codetruth-0.2.0/codetruth/languages/go/__init__.py +1 -0
- codetruth-0.2.0/codetruth/languages/javascript/__init__.py +8 -0
- codetruth-0.2.0/codetruth/languages/javascript/edges.py +361 -0
- codetruth-0.2.0/codetruth/languages/javascript/extractor.py +402 -0
- codetruth-0.2.0/codetruth/languages/javascript/plugin.py +52 -0
- codetruth-0.2.0/codetruth/languages/javascript/rules.py +201 -0
- codetruth-0.2.0/codetruth/languages/python/__init__.py +0 -0
- codetruth-0.2.0/codetruth/languages/python/edges.py +474 -0
- codetruth-0.2.0/codetruth/languages/python/extractor.py +271 -0
- codetruth-0.2.0/codetruth/languages/python/plugin.py +49 -0
- codetruth-0.2.0/codetruth/languages/python/rules.py +332 -0
- codetruth-0.2.0/codetruth/mcp_server.py +138 -0
- codetruth-0.2.0/codetruth/rules/python/common_dynamic.yaml +70 -0
- codetruth-0.2.0/codetruth/rules/python/django.yaml +57 -0
- codetruth-0.2.0/codetruth/rules/python/fastapi.yaml +38 -0
- codetruth-0.2.0/codetruth/rules/python/orm_web.yaml +50 -0
- codetruth-0.2.0/codetruth/runtime/__init__.py +283 -0
- codetruth-0.2.0/codetruth.egg-info/PKG-INFO +215 -0
- codetruth-0.2.0/codetruth.egg-info/SOURCES.txt +51 -0
- codetruth-0.2.0/codetruth.egg-info/dependency_links.txt +1 -0
- codetruth-0.2.0/codetruth.egg-info/entry_points.txt +2 -0
- codetruth-0.2.0/codetruth.egg-info/requires.txt +17 -0
- codetruth-0.2.0/codetruth.egg-info/top_level.txt +1 -0
- codetruth-0.2.0/pyproject.toml +51 -0
- codetruth-0.2.0/setup.cfg +4 -0
- codetruth-0.2.0/tests/test_api_cli.py +51 -0
- codetruth-0.2.0/tests/test_cache.py +88 -0
- codetruth-0.2.0/tests/test_config_strict.py +115 -0
- codetruth-0.2.0/tests/test_deletion_plan.py +47 -0
- codetruth-0.2.0/tests/test_evidence.py +209 -0
- codetruth-0.2.0/tests/test_extractor.py +42 -0
- codetruth-0.2.0/tests/test_javascript.py +88 -0
- codetruth-0.2.0/tests/test_reporting.py +58 -0
- codetruth-0.2.0/tests/test_rule_packs.py +68 -0
- codetruth-0.2.0/tests/test_runtime.py +161 -0
- codetruth-0.2.0/tests/test_validation.py +78 -0
codetruth-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alaik Asif Sheik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
codetruth-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codetruth
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A verification layer that lets AI agents safely delete code in large codebases
|
|
5
|
+
Author: Alaik Asif Sheik
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AlaikAsif/CodeTruth
|
|
8
|
+
Project-URL: Repository, https://github.com/AlaikAsif/CodeTruth
|
|
9
|
+
Project-URL: Issues, https://github.com/AlaikAsif/CodeTruth/issues
|
|
10
|
+
Keywords: dead-code,static-analysis,ai-agents,mcp,deletion-safety
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: networkx>=3.0
|
|
23
|
+
Requires-Dist: PyYAML>=6.0
|
|
24
|
+
Requires-Dist: tomli>=2.0; python_version < "3.11"
|
|
25
|
+
Provides-Extra: mcp
|
|
26
|
+
Requires-Dist: mcp>=1.2; extra == "mcp"
|
|
27
|
+
Provides-Extra: javascript
|
|
28
|
+
Requires-Dist: tree-sitter>=0.21; extra == "javascript"
|
|
29
|
+
Requires-Dist: tree-sitter-language-pack>=0.6; extra == "javascript"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: tree-sitter>=0.21; extra == "dev"
|
|
33
|
+
Requires-Dist: tree-sitter-language-pack>=0.6; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# CodeTruth
|
|
37
|
+
|
|
38
|
+
[](https://github.com/AlaikAsif/CodeTruth/actions/workflows/ci.yml)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
[](pyproject.toml)
|
|
41
|
+
|
|
42
|
+
**A verification layer that lets AI agents safely delete code in large codebases.**
|
|
43
|
+
|
|
44
|
+
Agents hallucinate absence of usage. CodeTruth inverts the question — instead of
|
|
45
|
+
*"is this code used?"* it asks **"can we prove this code is used?"** — and only
|
|
46
|
+
surfaces a symbol for deletion when it fails to find *any* usage path: no call,
|
|
47
|
+
no import, no inheritance, no string reference, no reflection target, no
|
|
48
|
+
framework registration. Detection is deterministic; the agent only reads the
|
|
49
|
+
evidence and decides. It is a **risk assessor for code deletion**, not a dead
|
|
50
|
+
code detector.
|
|
51
|
+
|
|
52
|
+
## Statuses
|
|
53
|
+
|
|
54
|
+
| Status | Meaning | Recommended action |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `safe_to_delete` | zero usage paths found under every analysis rule, **and** the name verified absent from all repo text outside its own definition | `delete` |
|
|
57
|
+
| `likely_dead` | no usage found, but external exposure can't be ruled out (public API, module, test-only) | `review_required` |
|
|
58
|
+
| `uncertain_dynamic_risk` | weak evidence exists (string refs, reflection, dynamic module) | `review_required` |
|
|
59
|
+
| `definitely_used` | strong reference or framework entry point proven | `keep` |
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install -e . # from this repo
|
|
65
|
+
pip install -e .[mcp] # with the MCP server
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## MCP (the primary interface — for agents)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
claude mcp add codetruth -- codetruth mcp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Tools exposed: `scan(repo_path, ...)` and `check_deletion_safety(repo_path, symbol)`.
|
|
75
|
+
The agent workflow: identify symbol → call `check_deletion_safety` → only delete
|
|
76
|
+
on `safe_to_delete`; everything else routes to human review.
|
|
77
|
+
|
|
78
|
+
## CLI
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
codetruth scan ./repo # review queue, strongest candidates first
|
|
82
|
+
codetruth scan ./repo -v --json out.json # full evidence
|
|
83
|
+
codetruth scan ./repo --app-mode # application (not library) repos:
|
|
84
|
+
# public symbols may be safe_to_delete
|
|
85
|
+
codetruth scan ./repo --strict # flag orphaned "useless clumps"
|
|
86
|
+
codetruth scan ./repo --min-rank 0.5 --group # trim the tail, group by file
|
|
87
|
+
codetruth scan ./repo --html report.html # self-contained HTML report
|
|
88
|
+
codetruth scan ./repo --ci # exit 1 if dead code exists (report gate)
|
|
89
|
+
codetruth check ./repo pkg.module:func # one symbol's evidence record
|
|
90
|
+
codetruth plan ./repo pkg.module:func # advisory deletion plan (never applied)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The `--ci` gate is advisory like everything else: it *fails the build* so a
|
|
94
|
+
human looks at provably-dead code — it never deletes. Mark false alarms with
|
|
95
|
+
`# codetruth: keep` or a `.codetruth.toml` entrypoint.
|
|
96
|
+
|
|
97
|
+
## Python API
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from codetruth import scan, check_deletion_safety
|
|
101
|
+
|
|
102
|
+
result = scan("./repo")
|
|
103
|
+
for rec in result.candidates():
|
|
104
|
+
print(rec.status.value, rec.symbol, rec.evidence_against_deletion)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Runtime evidence (v1.5)
|
|
108
|
+
|
|
109
|
+
Static analysis can't see cross-service usage (HTTP calls, queues, cron in
|
|
110
|
+
other repos). `@codetruth.track` logs real invocations in production:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import codetruth
|
|
114
|
+
|
|
115
|
+
@codetruth.track
|
|
116
|
+
def maybe_dead(): ...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or instrument a whole package with zero source edits:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
import codetruth.runtime
|
|
123
|
+
codetruth.runtime.instrument_package("myapp") # or CODETRUTH_AUTOTRACK=myapp
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Then feed the trace back: `codetruth scan ./repo --runtime-log runtime.jsonl`.
|
|
127
|
+
Observed calls promote a symbol to `definitely_used`; *"0 calls over N days"*
|
|
128
|
+
becomes the strongest evidence tier for deletion.
|
|
129
|
+
|
|
130
|
+
Tracing is production-safe: each process writes its own `runtime-<pid>.jsonl`
|
|
131
|
+
(merged at read — no lock contention between workers), and a daemon thread
|
|
132
|
+
flushes counts every `$CODETRUTH_FLUSH_INTERVAL` seconds (default 60), so
|
|
133
|
+
long-running servers land evidence without a clean exit.
|
|
134
|
+
|
|
135
|
+
## Finding useless clumps (strict reachability)
|
|
136
|
+
|
|
137
|
+
`codetruth scan ./repo --strict` asks a harder question: *is this code
|
|
138
|
+
reachable from any real entry point* (HTTP route, CLI command, `__main__`,
|
|
139
|
+
test, declared entrypoint)? Code that is internally well-connected — functions
|
|
140
|
+
calling each other — but never reached from an entry point surfaces as an
|
|
141
|
+
orphaned clump, with every member carrying a `cluster` field listing its
|
|
142
|
+
fellow members so the whole island can be reviewed (and deleted) as a group.
|
|
143
|
+
Dead-cluster grouping also applies in default mode whenever unreachable
|
|
144
|
+
symbols reference each other.
|
|
145
|
+
|
|
146
|
+
## Configuration (`.codetruth.toml`)
|
|
147
|
+
|
|
148
|
+
Teach the scanner about usage it can't see:
|
|
149
|
+
|
|
150
|
+
```toml
|
|
151
|
+
[codetruth]
|
|
152
|
+
app_mode = true # public symbols are internal (application)
|
|
153
|
+
entrypoints = [ # externally-reached symbols (cron, RPC, ...)
|
|
154
|
+
"jobs.nightly:run",
|
|
155
|
+
"services.handlers.*",
|
|
156
|
+
]
|
|
157
|
+
ignore_paths = ["migrations/", "vendor/**"]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Inline: a `# codetruth: keep` comment on (or above) a definition marks it as
|
|
161
|
+
an entry point.
|
|
162
|
+
|
|
163
|
+
## Deletion plans (advisory)
|
|
164
|
+
|
|
165
|
+
`codetruth plan ./repo pkg.mod:symbol` (also the `plan_deletion` MCP tool, and
|
|
166
|
+
attached automatically to every `safe_to_delete` record) describes exactly what
|
|
167
|
+
a removal would involve: the decorator-to-end source span, imports that become
|
|
168
|
+
orphaned, and any `__all__` entry. CodeTruth **never applies** a plan — it is
|
|
169
|
+
information for whoever decides.
|
|
170
|
+
|
|
171
|
+
## Review-queue ranking
|
|
172
|
+
|
|
173
|
+
Every record carries a `rank_score` in `[0, 1]` — a deterministic ordering
|
|
174
|
+
heuristic (not a calibrated probability; see PLAN.md §4). Higher means weaker
|
|
175
|
+
evidence of use, so `scan()` and the CLI surface the strongest deletion
|
|
176
|
+
targets first. Within `uncertain_dynamic_risk` it separates a lone
|
|
177
|
+
string-literal reference from forty fuzzy attribute-name matches, so a big
|
|
178
|
+
review queue is triageable instead of flat.
|
|
179
|
+
|
|
180
|
+
## Performance
|
|
181
|
+
|
|
182
|
+
Scans are cached at `<repo>/.codetruth/index.json`, keyed by a fingerprint of
|
|
183
|
+
every source and config file's `(mtime, size)`. An unchanged repo returns the
|
|
184
|
+
cached result (≈15× faster on an 8k-symbol repo); any file change triggers a
|
|
185
|
+
full rescan. The cache never patches the graph incrementally — a stale
|
|
186
|
+
cross-file edge could mask a real usage path, so correctness always wins.
|
|
187
|
+
Bypass with `--no-cache` (CLI) or `force_rescan` (MCP). Add `.codetruth/` to
|
|
188
|
+
`.gitignore`.
|
|
189
|
+
|
|
190
|
+
## Architecture
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Layer 1 Symbol Extraction codetruth/languages/python/extractor.py
|
|
194
|
+
Layer 2 Relationship Graph codetruth/languages/python/edges.py (strong/weak edges)
|
|
195
|
+
Layer 3 Semantic Rules codetruth/languages/python/rules.py + codetruth/rules/python/*.yaml
|
|
196
|
+
Layer 4 Evidence + Decision codetruth/core/evidence.py (4-way status)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The core engine is language-agnostic (`codetruth/core/`, `LanguagePlugin`
|
|
200
|
+
interface). **Python** is the full v1 plugin (FastAPI, Django, Celery, click,
|
|
201
|
+
pytest, SQLAlchemy, Typer rule coverage). **JavaScript/TypeScript** is a beta
|
|
202
|
+
plugin (`pip install codetruth[javascript]`, then `scan --language javascript`):
|
|
203
|
+
tree-sitter extraction, ESM/CommonJS import resolution, package.json
|
|
204
|
+
entry points, string/config wiring, eval poisoning, and external-base cautions
|
|
205
|
+
— with the shared evidence, ranking, cluster, backstop, and cache layers
|
|
206
|
+
working unchanged. Go remains a stub.
|
|
207
|
+
|
|
208
|
+
## Known limitations
|
|
209
|
+
|
|
210
|
+
- Cross-service usage is invisible to static analysis alone — runtime tracing
|
|
211
|
+
is the partial fix.
|
|
212
|
+
- 100% certainty is impossible; `safe_to_delete` means "no usage path found
|
|
213
|
+
under the defined rules," not a mathematical proof.
|
|
214
|
+
- Framework rule coverage (Layer 3) is a maintained knowledge base, never
|
|
215
|
+
finished. New rules go in `codetruth/rules/python/*.yaml` — no code changes.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# CodeTruth
|
|
2
|
+
|
|
3
|
+
[](https://github.com/AlaikAsif/CodeTruth/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](pyproject.toml)
|
|
6
|
+
|
|
7
|
+
**A verification layer that lets AI agents safely delete code in large codebases.**
|
|
8
|
+
|
|
9
|
+
Agents hallucinate absence of usage. CodeTruth inverts the question — instead of
|
|
10
|
+
*"is this code used?"* it asks **"can we prove this code is used?"** — and only
|
|
11
|
+
surfaces a symbol for deletion when it fails to find *any* usage path: no call,
|
|
12
|
+
no import, no inheritance, no string reference, no reflection target, no
|
|
13
|
+
framework registration. Detection is deterministic; the agent only reads the
|
|
14
|
+
evidence and decides. It is a **risk assessor for code deletion**, not a dead
|
|
15
|
+
code detector.
|
|
16
|
+
|
|
17
|
+
## Statuses
|
|
18
|
+
|
|
19
|
+
| Status | Meaning | Recommended action |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `safe_to_delete` | zero usage paths found under every analysis rule, **and** the name verified absent from all repo text outside its own definition | `delete` |
|
|
22
|
+
| `likely_dead` | no usage found, but external exposure can't be ruled out (public API, module, test-only) | `review_required` |
|
|
23
|
+
| `uncertain_dynamic_risk` | weak evidence exists (string refs, reflection, dynamic module) | `review_required` |
|
|
24
|
+
| `definitely_used` | strong reference or framework entry point proven | `keep` |
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e . # from this repo
|
|
30
|
+
pip install -e .[mcp] # with the MCP server
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## MCP (the primary interface — for agents)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
claude mcp add codetruth -- codetruth mcp
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Tools exposed: `scan(repo_path, ...)` and `check_deletion_safety(repo_path, symbol)`.
|
|
40
|
+
The agent workflow: identify symbol → call `check_deletion_safety` → only delete
|
|
41
|
+
on `safe_to_delete`; everything else routes to human review.
|
|
42
|
+
|
|
43
|
+
## CLI
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
codetruth scan ./repo # review queue, strongest candidates first
|
|
47
|
+
codetruth scan ./repo -v --json out.json # full evidence
|
|
48
|
+
codetruth scan ./repo --app-mode # application (not library) repos:
|
|
49
|
+
# public symbols may be safe_to_delete
|
|
50
|
+
codetruth scan ./repo --strict # flag orphaned "useless clumps"
|
|
51
|
+
codetruth scan ./repo --min-rank 0.5 --group # trim the tail, group by file
|
|
52
|
+
codetruth scan ./repo --html report.html # self-contained HTML report
|
|
53
|
+
codetruth scan ./repo --ci # exit 1 if dead code exists (report gate)
|
|
54
|
+
codetruth check ./repo pkg.module:func # one symbol's evidence record
|
|
55
|
+
codetruth plan ./repo pkg.module:func # advisory deletion plan (never applied)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The `--ci` gate is advisory like everything else: it *fails the build* so a
|
|
59
|
+
human looks at provably-dead code — it never deletes. Mark false alarms with
|
|
60
|
+
`# codetruth: keep` or a `.codetruth.toml` entrypoint.
|
|
61
|
+
|
|
62
|
+
## Python API
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from codetruth import scan, check_deletion_safety
|
|
66
|
+
|
|
67
|
+
result = scan("./repo")
|
|
68
|
+
for rec in result.candidates():
|
|
69
|
+
print(rec.status.value, rec.symbol, rec.evidence_against_deletion)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Runtime evidence (v1.5)
|
|
73
|
+
|
|
74
|
+
Static analysis can't see cross-service usage (HTTP calls, queues, cron in
|
|
75
|
+
other repos). `@codetruth.track` logs real invocations in production:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import codetruth
|
|
79
|
+
|
|
80
|
+
@codetruth.track
|
|
81
|
+
def maybe_dead(): ...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or instrument a whole package with zero source edits:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import codetruth.runtime
|
|
88
|
+
codetruth.runtime.instrument_package("myapp") # or CODETRUTH_AUTOTRACK=myapp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then feed the trace back: `codetruth scan ./repo --runtime-log runtime.jsonl`.
|
|
92
|
+
Observed calls promote a symbol to `definitely_used`; *"0 calls over N days"*
|
|
93
|
+
becomes the strongest evidence tier for deletion.
|
|
94
|
+
|
|
95
|
+
Tracing is production-safe: each process writes its own `runtime-<pid>.jsonl`
|
|
96
|
+
(merged at read — no lock contention between workers), and a daemon thread
|
|
97
|
+
flushes counts every `$CODETRUTH_FLUSH_INTERVAL` seconds (default 60), so
|
|
98
|
+
long-running servers land evidence without a clean exit.
|
|
99
|
+
|
|
100
|
+
## Finding useless clumps (strict reachability)
|
|
101
|
+
|
|
102
|
+
`codetruth scan ./repo --strict` asks a harder question: *is this code
|
|
103
|
+
reachable from any real entry point* (HTTP route, CLI command, `__main__`,
|
|
104
|
+
test, declared entrypoint)? Code that is internally well-connected — functions
|
|
105
|
+
calling each other — but never reached from an entry point surfaces as an
|
|
106
|
+
orphaned clump, with every member carrying a `cluster` field listing its
|
|
107
|
+
fellow members so the whole island can be reviewed (and deleted) as a group.
|
|
108
|
+
Dead-cluster grouping also applies in default mode whenever unreachable
|
|
109
|
+
symbols reference each other.
|
|
110
|
+
|
|
111
|
+
## Configuration (`.codetruth.toml`)
|
|
112
|
+
|
|
113
|
+
Teach the scanner about usage it can't see:
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[codetruth]
|
|
117
|
+
app_mode = true # public symbols are internal (application)
|
|
118
|
+
entrypoints = [ # externally-reached symbols (cron, RPC, ...)
|
|
119
|
+
"jobs.nightly:run",
|
|
120
|
+
"services.handlers.*",
|
|
121
|
+
]
|
|
122
|
+
ignore_paths = ["migrations/", "vendor/**"]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Inline: a `# codetruth: keep` comment on (or above) a definition marks it as
|
|
126
|
+
an entry point.
|
|
127
|
+
|
|
128
|
+
## Deletion plans (advisory)
|
|
129
|
+
|
|
130
|
+
`codetruth plan ./repo pkg.mod:symbol` (also the `plan_deletion` MCP tool, and
|
|
131
|
+
attached automatically to every `safe_to_delete` record) describes exactly what
|
|
132
|
+
a removal would involve: the decorator-to-end source span, imports that become
|
|
133
|
+
orphaned, and any `__all__` entry. CodeTruth **never applies** a plan — it is
|
|
134
|
+
information for whoever decides.
|
|
135
|
+
|
|
136
|
+
## Review-queue ranking
|
|
137
|
+
|
|
138
|
+
Every record carries a `rank_score` in `[0, 1]` — a deterministic ordering
|
|
139
|
+
heuristic (not a calibrated probability; see PLAN.md §4). Higher means weaker
|
|
140
|
+
evidence of use, so `scan()` and the CLI surface the strongest deletion
|
|
141
|
+
targets first. Within `uncertain_dynamic_risk` it separates a lone
|
|
142
|
+
string-literal reference from forty fuzzy attribute-name matches, so a big
|
|
143
|
+
review queue is triageable instead of flat.
|
|
144
|
+
|
|
145
|
+
## Performance
|
|
146
|
+
|
|
147
|
+
Scans are cached at `<repo>/.codetruth/index.json`, keyed by a fingerprint of
|
|
148
|
+
every source and config file's `(mtime, size)`. An unchanged repo returns the
|
|
149
|
+
cached result (≈15× faster on an 8k-symbol repo); any file change triggers a
|
|
150
|
+
full rescan. The cache never patches the graph incrementally — a stale
|
|
151
|
+
cross-file edge could mask a real usage path, so correctness always wins.
|
|
152
|
+
Bypass with `--no-cache` (CLI) or `force_rescan` (MCP). Add `.codetruth/` to
|
|
153
|
+
`.gitignore`.
|
|
154
|
+
|
|
155
|
+
## Architecture
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
Layer 1 Symbol Extraction codetruth/languages/python/extractor.py
|
|
159
|
+
Layer 2 Relationship Graph codetruth/languages/python/edges.py (strong/weak edges)
|
|
160
|
+
Layer 3 Semantic Rules codetruth/languages/python/rules.py + codetruth/rules/python/*.yaml
|
|
161
|
+
Layer 4 Evidence + Decision codetruth/core/evidence.py (4-way status)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The core engine is language-agnostic (`codetruth/core/`, `LanguagePlugin`
|
|
165
|
+
interface). **Python** is the full v1 plugin (FastAPI, Django, Celery, click,
|
|
166
|
+
pytest, SQLAlchemy, Typer rule coverage). **JavaScript/TypeScript** is a beta
|
|
167
|
+
plugin (`pip install codetruth[javascript]`, then `scan --language javascript`):
|
|
168
|
+
tree-sitter extraction, ESM/CommonJS import resolution, package.json
|
|
169
|
+
entry points, string/config wiring, eval poisoning, and external-base cautions
|
|
170
|
+
— with the shared evidence, ranking, cluster, backstop, and cache layers
|
|
171
|
+
working unchanged. Go remains a stub.
|
|
172
|
+
|
|
173
|
+
## Known limitations
|
|
174
|
+
|
|
175
|
+
- Cross-service usage is invisible to static analysis alone — runtime tracing
|
|
176
|
+
is the partial fix.
|
|
177
|
+
- 100% certainty is impossible; `safe_to_delete` means "no usage path found
|
|
178
|
+
under the defined rules," not a mathematical proof.
|
|
179
|
+
- Framework rule coverage (Layer 3) is a maintained knowledge base, never
|
|
180
|
+
finished. New rules go in `codetruth/rules/python/*.yaml` — no code changes.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""CodeTruth — a verification layer that lets AI agents safely delete code.
|
|
2
|
+
|
|
3
|
+
from codetruth import scan, check_deletion_safety, track
|
|
4
|
+
"""
|
|
5
|
+
from .api import check_deletion_safety, plan_deletion, scan
|
|
6
|
+
from .core.models import (Action, Edge, EvidenceRecord, RiskLevel, Status,
|
|
7
|
+
Symbol)
|
|
8
|
+
from .runtime import track
|
|
9
|
+
|
|
10
|
+
__version__ = "0.2.0"
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"scan", "check_deletion_safety", "plan_deletion", "track",
|
|
14
|
+
"Status", "RiskLevel", "Action", "Symbol", "Edge", "EvidenceRecord",
|
|
15
|
+
"__version__",
|
|
16
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Programmatic API — what the MCP server and scripts call.
|
|
2
|
+
|
|
3
|
+
from codetruth import scan, check_deletion_safety, plan_deletion
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .core.deletion import build_deletion_plan
|
|
11
|
+
from .core.plugin import get_plugin
|
|
12
|
+
from .core.scanner import ScanResult, scan_repo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def scan(repo_path: str | Path, language: str = "python",
|
|
16
|
+
treat_public_as_api: Optional[bool] = None,
|
|
17
|
+
runtime_log: Optional[str | Path] = None,
|
|
18
|
+
use_cache: bool = True,
|
|
19
|
+
reachability: str = "default") -> ScanResult:
|
|
20
|
+
"""Run all four layers over a repository and return the evidence set.
|
|
21
|
+
|
|
22
|
+
treat_public_as_api: True (conservative, the default when neither the
|
|
23
|
+
caller nor .codetruth.toml says otherwise) caps unreferenced public
|
|
24
|
+
symbols at `likely_dead` because a library's consumers are invisible.
|
|
25
|
+
False for application code; None defers to `app_mode` in .codetruth.toml.
|
|
26
|
+
|
|
27
|
+
reachability: "default", or "strict" to detect useless clumps — code
|
|
28
|
+
that is internally connected but never reached from any real entry point
|
|
29
|
+
(route, CLI command, __main__, test, declared entrypoint). Strict-mode
|
|
30
|
+
findings surface in the review queue with cluster grouping.
|
|
31
|
+
|
|
32
|
+
use_cache=True reuses a persisted result (<repo>/.codetruth/index.json)
|
|
33
|
+
when no source or config file has changed since the last scan.
|
|
34
|
+
"""
|
|
35
|
+
return scan_repo(repo_path, language=language,
|
|
36
|
+
treat_public_as_api=treat_public_as_api,
|
|
37
|
+
runtime_log=runtime_log, use_cache=use_cache,
|
|
38
|
+
reachability=reachability)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def check_deletion_safety(repo_path: str | Path, symbol: str,
|
|
42
|
+
result: Optional[ScanResult] = None,
|
|
43
|
+
**scan_kwargs) -> dict:
|
|
44
|
+
"""The agent-facing question: 'may I delete this symbol?'
|
|
45
|
+
|
|
46
|
+
Returns the evidence record(s) for the symbol, or candidate matches if
|
|
47
|
+
the name is ambiguous. The agent should only act on `safe_to_delete`.
|
|
48
|
+
"""
|
|
49
|
+
if result is None:
|
|
50
|
+
result = scan(repo_path, **scan_kwargs)
|
|
51
|
+
matches = result.find(symbol)
|
|
52
|
+
if not matches:
|
|
53
|
+
return {
|
|
54
|
+
"found": False, "symbol": symbol,
|
|
55
|
+
"message": "Symbol not found in the scanned repository. "
|
|
56
|
+
"Do NOT delete based on this response — verify the "
|
|
57
|
+
"symbol id (format: 'pkg.module:Qual.name').",
|
|
58
|
+
}
|
|
59
|
+
if len(matches) > 1:
|
|
60
|
+
return {
|
|
61
|
+
"found": True, "ambiguous": True, "symbol": symbol,
|
|
62
|
+
"message": f"{len(matches)} symbols match — re-query with an exact id.",
|
|
63
|
+
"candidates": [m.to_dict() for m in matches[:20]],
|
|
64
|
+
}
|
|
65
|
+
record = matches[0]
|
|
66
|
+
return {"found": True, "ambiguous": False, "record": record.to_dict()}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def plan_deletion(repo_path: str | Path, symbol: str,
|
|
70
|
+
language: str = "python",
|
|
71
|
+
result: Optional[ScanResult] = None,
|
|
72
|
+
**scan_kwargs) -> dict:
|
|
73
|
+
"""Advisory plan for removing one symbol: exact span, orphaned imports,
|
|
74
|
+
__all__ entry. Works for any status — the response carries the verdict so
|
|
75
|
+
the reader knows whether acting on the plan is advised. CodeTruth never
|
|
76
|
+
applies the plan.
|
|
77
|
+
"""
|
|
78
|
+
safety = check_deletion_safety(repo_path, symbol, result=result,
|
|
79
|
+
**scan_kwargs)
|
|
80
|
+
if not safety.get("found") or safety.get("ambiguous"):
|
|
81
|
+
return safety
|
|
82
|
+
|
|
83
|
+
record = safety["record"]
|
|
84
|
+
if record.get("deletion_plan"):
|
|
85
|
+
plan = record["deletion_plan"]
|
|
86
|
+
else:
|
|
87
|
+
repo = Path(repo_path).resolve()
|
|
88
|
+
plugin = get_plugin(language)
|
|
89
|
+
modules, _warnings = plugin.extract(repo)
|
|
90
|
+
sym = next((s for m in modules for s in m.symbols
|
|
91
|
+
if s.id == record["symbol"]), None)
|
|
92
|
+
plan = build_deletion_plan(repo, sym) if sym is not None else None
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
"found": True, "symbol": record["symbol"],
|
|
96
|
+
"status": record["status"],
|
|
97
|
+
"recommended_action": record["recommended_action"],
|
|
98
|
+
"plan": plan,
|
|
99
|
+
"warning": None if record["status"] == "safe_to_delete" else
|
|
100
|
+
f"status is {record['status']} — this plan is informational; "
|
|
101
|
+
"review is required before anyone acts on it",
|
|
102
|
+
}
|