Seamcheck
Your AI wrote 400 lines. Which of them are actually wired to anything?
Seamcheck reads a Django + JavaScript project and tells you what connects to what — which
fetch() lands on which view, which template element the JS is reaching for, which CSS
rule nothing has referenced since 2023. Then it tells you what it couldn't work out,
which turns out to be the part that matters.
No SaaS. No upload. One command, one HTML file, and an exit code for CI.
The four answers
Every dead-code tool ever written has told you something was unused and been wrong, and you stopped trusting it. Seamcheck has a fourth answer:
connected |
something reaches this — here's the file and line |
unresolved |
something reaches for this and it isn't there |
unused |
both ends are observable and nothing uses it |
uncertain |
no evidence either way. Not a claim it's dead. |
That last row is the whole product. A page reached by an <a href> looks exactly like a
dead one if you only parse fetch() calls. Seamcheck says so instead of guessing. On a
700-URL project that's the difference between a report you act on and 668 lies.
It also reports its own coverage — how much of each file it actually reasoned about. No other tool I've found will tell you where it wasn't looking.
What it found in the project it was built on
Not hypotheticals. Real bugs, in a real 36,000-symbol codebase, found while writing this:
- Five CSS custom properties in a loaded stylesheet that resolve to nothing —
--text-primary,--border-colorand friends. Those declarations render nothing today. - An endpoint reported missing that turned out to be
<str:division_id>matching a deliberate'all'sentinel — so I fixed the matcher instead of "fixing" the code. - 65 API routes whose path appears in no source file at all.
- 46 DOM elements written by more than one module — the reason a display bug survives being "fixed" in one of them.
Install
pip install seamcheck
Then two things in settings.py:
INSTALLED_APPS = [..., "seamcheck"]
SEAMCHECK_CONFIG = {
"urlconf_module": "myproject.urls",
"templates_root": "myapp/templates",
"js_source_root": "myapp/static/js",
"css_source_root": "myapp/static/css",
"first_party_prefixes": ["myapp", "myproject"],
}
Every project-specific path lives in that dict. Nothing is hardcoded anywhere in the extractors — which is how this was lifted out of the project it grew in without touching a line of it.
You need Node on PATH. The JS and CSS parsers run on it. You do not need npm or
node_modules — acorn and postcss ship inlined in the wheel. If Node is missing,
Seamcheck says so and gives you the Python half rather than dying.
Use
python manage.py seamcheck # scan, summary, write the map
python manage.py seamcheck --check # exit 1 on new findings. This is the CI one.
python manage.py seamcheck --format map # the UI, one self-contained HTML file
python manage.py seamcheck --explain <id> # one symbol, with the code around it
The UI
One file, no network, opens on a phone. A left rail of views; a canvas that draws every symbol a page touches at once — 1,366 of them on the biggest page here — with the broken ones filled in red so they find you rather than the other way round.
Click any node and it lights the line through it: page → module → fetch() → URL →
view, each hop with the real source, and a button to show the whole enclosing function.
Or isolate that one chain and drop everything else.
There's a Files view too — your actual folder tree, with a bar per file showing how many of its declarations Seamcheck reasoned about. Because "no findings" and "never looked" are not the same sentence.
python manage.py seamcheck --format map --serve # open it from your phone
python manage.py seamcheck --format map --serve --tunnel # ...from anywhere
Nothing is uploaded. --serve is a socket on your machine that dies with the command.
Per-commit
python manage.py seamcheck --backfill 20 # scan the last 20 commits
Now the map has a commit picker. Pick one and see what that commit changed — added, removed, status flipped — including things it deleted, which no longer exist to be drawn and get named instead.
CI
python manage.py seamcheck --check --since $BASE_SHA
1 = new findings. 2 = no baseline, so the gate didn't run. 0 = clean. That
distinction matters: a gate that never ran is not a gate that passed.
Agents
seamcheck_check, seamcheck_report, seamcheck_explain, seamcheck_triage over MCP,
and an AGENTS.md that tells your agent the one rule that matters: never delete
something because it's uncertain.
What it can't do
Written down because a tool that hides its blind spots is worse than no tool:
- Django + vanilla JS. No React, Vue, or TypeScript yet.
- Celery, Redis, WebSockets and Stripe aren't traced. Anything reached only through those is invisible, and the UI says so rather than showing a confident zero.
- A URL built at runtime stays
uncertain. The prefix is recorded, never a guess. - It has been run against one real project. Mine. That's one more than most tools at this stage and far fewer than you'd want.
Contributing
Issues and PRs welcome. One house rule, and it's the reason the tool is worth anything:
never make a claim the scan can't evidence. If you can't prove it, it's uncertain,
and the note says which evidence source was missing.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file seamcheck-0.1.0.tar.gz.
File metadata
- Download URL: seamcheck-0.1.0.tar.gz
- Upload date:
- Size: 219.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b2fff2e3206aad086afaf6480994962b80913495bd88a5a038a326bcaa0e6c6
|
|
| MD5 |
3878f49ec7e42a1376cb0d24e24a6775
|
|
| BLAKE2b-256 |
52be16d91f34f2a7bb6c71235753661fa2b83aef4470b813136ecc39d3d82605
|
Provenance
The following attestation bundles were made for seamcheck-0.1.0.tar.gz:
Publisher:
release.yml on dardameiz/seamcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seamcheck-0.1.0.tar.gz -
Subject digest:
6b2fff2e3206aad086afaf6480994962b80913495bd88a5a038a326bcaa0e6c6 - Sigstore transparency entry: 2655429125
- Sigstore integration time:
-
Permalink:
dardameiz/seamcheck@3434d67ebdb85942d20d1105bb09c052c1df5dea -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dardameiz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3434d67ebdb85942d20d1105bb09c052c1df5dea -
Trigger Event:
push
-
Statement type:
File details
Details for the file seamcheck-0.1.0-py3-none-any.whl.
File metadata
- Download URL: seamcheck-0.1.0-py3-none-any.whl
- Upload date:
- Size: 192.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6344cb3c2625cf92b909438494d35ee4543a6f02a7e6a3892dde5448d337b89
|
|
| MD5 |
bce6057659188d180197bbfa0089c713
|
|
| BLAKE2b-256 |
8d6aa41e95d3bb02ce999927977865a6d4e43a7ed53ed458d54743bf2d31fe7e
|
Provenance
The following attestation bundles were made for seamcheck-0.1.0-py3-none-any.whl:
Publisher:
release.yml on dardameiz/seamcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seamcheck-0.1.0-py3-none-any.whl -
Subject digest:
b6344cb3c2625cf92b909438494d35ee4543a6f02a7e6a3892dde5448d337b89 - Sigstore transparency entry: 2655429135
- Sigstore integration time:
-
Permalink:
dardameiz/seamcheck@3434d67ebdb85942d20d1105bb09c052c1df5dea -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dardameiz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3434d67ebdb85942d20d1105bb09c052c1df5dea -
Trigger Event:
push
-
Statement type: