DEV Community

Cover image for We Deleted `pip install` From a Dev Tool. Here's Everything That Broke.
Rajpriyan S
Rajpriyan S

Posted on

We Deleted `pip install` From a Dev Tool. Here's Everything That Broke.

Submitted for the **Side Quest · The Write-Up* — judged on insight, not audience size. This post covers, in order: what we reimplemented, what the standard library made genuinely painful, the package our own code made look unnecessary, and the specific edge case that cost us real debugging time.*

CodeIntel Doctor Zero — a single-file, offline codebase doctor that scores your repo's health, catches leaked secrets, finds duplicate code, and tells you exactly what to fix first. Built for Zero Dependency Hack 2026, Track A — Developer Tools & CLI, with zero third-party runtime dependencies, 87 passing tests, and a bit-for-bit reproducible build.

CodeIntel Doctor Zero overview dashboard, dark theme, showing a 57/100 health score, file stats, and a prioritized findings list

The local browser UI, rendered from the project's real ui.html against the repo's own published fixture data — no mockup tool, this is the actual product.


The one-line pitch

Point it at any repository. In under a second, it hands you a 0–100 health score you can actually audit line by line, a prioritized "fix this first" list, an offline secret scanner, a real duplicate-code detector, local search, and a self-contained HTML report — all from one Python file, running on nothing but the standard library.

git clone https://github.com/rajpriyanid-creator/codeintel-doctor
cd codeintel-doctor-zero
python3 build.py          # verifies runtime, runs 87 tests, builds a deterministic artifact
python3 codeintel.py analyze .
Enter fullscreen mode Exit fullscreen mode

No pip install. No Docker. No API key. No internet connection — ever.


The constraint that started it

Every dev-tool hackathon has the same shortcut available:

pip install click rich pygments radon chardet jinja2 pytest ...
Enter fullscreen mode Exit fullscreen mode

Seven or eight battle-tested libraries, and you're 80% of the way to a polished CLI before lunch.

Zero Dependency Hack 2026 removed that shortcut on purpose. Track A's brief was blunt: build a real developer tool with zero third-party runtime dependencies.

That reframes the actual question. It's no longer:

Can Python scan a repository?

Obviously — yes. The real question is:

How much genuine repository intelligence can you build when the entire package ecosystem is off the table — and can you prove it, not just claim it?

That second half — prove it — is what separates a gimmick from an engineering submission. So the project treats "zero dependencies" as a claim with receipts, not a badge in a README. Running verify_zero_deps.py parses codeintel.py's actual import statements with Python's own ast module, checks every one against sys.stdlib_module_names, scans for subprocess calls, scans for network-capable imports, and scans for vendored packages. The real, generated output — captured in deps-proof.txt — ends with:

ZERO-DEPENDENCY CHECK: PASS
THIRD-PARTY RUNTIME DEPS: 0
Enter fullscreen mode Exit fullscreen mode

Anyone can clone the repo and run that check themselves. That's the whole point.


What we would normally have installed

We didn't try to rebuild entire ecosystems — only the exact slice each capability needed.

Capability Typical dependency Stdlib foundation What we actually built
CLI parsing click / typer argparse commands, flags, validation, CI-ready exit codes
Terminal styling rich / colorama sys, os, raw ANSI SGR TTY-aware severity coloring + NO_COLOR fallback
Tables tabulate strings + terminal I/O deterministic table renderer
Repo traversal pathspec / glob os.walk, fnmatch, pathlib ignore rules, symlink-loop safety, depth limits
Encoding detection chardet UTF-8 probing + fallback text/binary classification
Language ID pygments extensions + shebangs heuristic language detection
Complexity radon / lizard re estimated control-flow complexity
Duplicate detection MinHash-style libs hashlib sliding-window fingerprint + verify
Secret scanning dedicated scanners re pattern rules, confidence, masking, baselines
HTML reports Jinja2 str.format self-contained, zero-asset HTML report
Test runner pytest unittest 87-test regression suite

STDLIB.md documents 17 real substitutions, each with an honest note on where the hand-rolled version is not a drop-in replacement. Our terminal renderer, for instance, never tries to become rich — it only does severity colors, bold headers, TTY detection, and a plain fallback. That's all the product needed, so that's all we built.


What the standard library made genuinely painful

The table above makes it look like every substitution was a clean swap. It wasn't. This is the part most "we went dependency-free!" posts skip, and it's the actual insight:

  • Color support isn't just "print ANSI codes." colorama/rich quietly handle Windows for you. The standard library does not. Legacy cmd.exe needs SetConsoleMode called on the console handle via ctypes before ANSI escapes render at all — otherwise a Windows user sees literal \x1b[36m garbage in their terminal instead of color. That's not in any stdlib color tutorial; you find it the first time someone runs your tool on a non-Windows-Terminal machine.
  • fnmatch is not .gitignore. pathspec gives you real gitignore semantics for free: negation patterns (!keep-this.py), directory-only patterns (build/), and ** globstars. fnmatch only does flat shell globs. Reimplementing "ignore like git ignores" correctly — including negation overriding an earlier broad match — took noticeably longer than the actual file-walking logic around it.
  • json.dumps is not deterministic by default. Dict key order, float repr, and separator whitespace can all vary in ways that silently break a byte-for-byte reproducibility test. Getting the determinism suite to pass meant explicitly forcing sort_keys=True, pinning separators, and controlling float formatting everywhere a score or percentage got serialized.
  • unittest has no pytest.mark.parametrize. Every "run this same check against 10 fixture variations" test became a hand-rolled loop with self.subTest(...), which works but is far more ceremony than a decorator.
  • There is no safe recursive directory walker built in. os.walk will happily follow a symlink into a loop unless you tell it not to — and even then, avoiding directory symlink loops specifically (as opposed to just not following symlinks at all) needs manual (st_dev, st_ino) tracking that a library like pathspec or scandir-based tools give you out of the box.

None of these are hard problems. They're just the fifty small taxes a package normally pays for you, silently, that become your problem the moment you remove it.


The package our own code made look unnecessary

If we had to name one: a dedicated secret-scanning tool — the detect-secrets / trufflehog category of product.

Those are real, well-built tools with entropy analysis, plugin architectures, and CI integrations. Ours is roughly 150 lines of re patterns, a confidence heuristic, and a masking function — and for the specific job of "tell a developer there might be a hardcoded credential before they commit it," it does the same practical job:

severity + confidence + file + line + masked evidence + suggested fix
Enter fullscreen mode Exit fullscreen mode

We're not claiming feature parity — no entropy scoring, no plugin system, no maintained rule database that updates as new credential formats appear. But the honest finding is that for the 80% case a small team actually needs — "did someone just commit a password" — you don't need to install a scanning product. You need about a page and a half of regular expressions and the discipline to always mask what you find. That's a genuinely useful thing to know before your next pip install.


Show, don't tell: the actual output

This isn't a mockup. This is what doctor --explain-score prints on a deliberately messy fixture repo — every point lost, every finding, generated from a real scan:

Terminal screenshot showing codeintel.py doctor output with a full health-score breakdown by dimension and a HIGH severity finding for an oversized module

That's the difference between analysis ("here's some data") and diagnosis ("here's what's wrong, why it matters, and what to do about it"). Every point lost is traceable to named, counted evidence — nothing is a magic number.

Compare that to the plain analyze summary on the same repo:

$ python3 codeintel.py analyze fixtures/messy-repo --plain
CODEINTEL DOCTOR ZERO
Repository: messy-repo
Health: 57/100

Files:               7
Lines:           1,014
Functions:          45
Classes:             0
Tests:               0

Languages
  Python          99.5%
  Markdown         0.5%

Top Risk Areas
  01 src/api/routes.py            68
  02 src/auth/login.py            10
  03 src/auth/register.py         10

Findings
  CRITICAL             0
  HIGH                 2
  MEDIUM              24
  LOW                  2
Enter fullscreen mode Exit fullscreen mode

And when you want it in a browser: the local UI

The CLI is the primary interface, but python3 codeintel.py ui . spins up the exact same analysis behind a single-page dashboard, served locally on 127.0.0.1 with zero network requests — nothing leaves your machine, and there's nothing to configure.

The sidebar mirrors the CLI one-to-one: Overview, Doctor, Security, Duplicates, Structure, Search, Explain file. Same data everywhere, just a different lens on it.

Overview — the health ring, per-dimension bars, the "needs attention" queue, and language mix, all from one scan:

CodeIntel overview dashboard with 57/100 health score circle, dimension bars for structure/maintainability/security/testing/documentation/duplication/hygiene, and a findings sidebar

Doctor — the same prioritized findings as doctor --explain-score, but as a sortable table with severity, location, and the suggested fix right next to it:

CodeIntel Doctor tab showing a table of findings ranked HIGH to LOW with file locations and recommendations

Security — masked evidence, confidence-scored signals, nothing phrased as a certainty:

CodeIntel Security tab showing counts of critical high medium and low findings and a table of masked credential evidence

Beyond these three, Duplicates shows the sliding-window fingerprint clusters with exact file:line ranges on both sides of every match, Structure surfaces likely entry points and a scanned-file table, Search does live client-side text/symbol lookup against the current scan, and Explain file drills into a single file's symbols, complexity, and findings.

The important architectural decision here isn't the UI polish — it's that the browser layer does not run a second analysis engine. It renders the exact same AnalysisReport object that the CLI and the JSON/HTML report use. That means:

CLI · JSON · HTML · Browser  →  one analysis model
Enter fullscreen mode Exit fullscreen mode

instead of four independent implementations that could quietly disagree with each other. One Export JSON button, and the browser view and the CLI's --json output are byte-for-byte the same data.


The hardest problem: what even counts as "duplicate code"?

A naive duplicate detector compares blocks of text directly — slow, and noisy. CodeIntel's pipeline instead does:

source → normalize → tokenize → sliding windows → SHA-256 fingerprints
       → candidate matches → content verification → duplicate clusters
Enter fullscreen mode Exit fullscreen mode

But the more interesting engineering decision isn't the algorithm — it's the honesty about its limits. The detector catches near-verbatim duplicates after normalization. It does not claim to catch semantically identical code with renamed variables, and it says so directly in its own docs. So the finding is phrased as:

"Possible duplicated block"

never:

"These two functions are semantically identical."

That restraint matters more than it sounds like it should. A tool that overclaims what it detects is a tool nobody trusts in CI.


Security scanning, without a security package

The scanner looks for the usual credential-shaped trouble: private keys, API-key-like strings, hardcoded passwords, credential-bearing URLs, JWT-like values, disabled TLS verification, debug configs left on. It's pure re pattern matching — no vulnerability database, no cloud lookup, and it never pretends otherwise. Every finding ships with severity, confidence, file, line, masked evidence, and a suggested fix — and the actual secret value is never written anywhere, in any output mode:

DB_PASSWORD = "hunt********rd2024"
Enter fullscreen mode Exit fullscreen mode

Because it makes zero network calls (mechanically verified, not assumed), your source code never leaves your machine while it's scanned. That's a genuine privacy property — but the docs are careful to separate that from a security guarantee: offline means private, not automatically safe. Teams can accept known findings with --baseline so CI only flags what's actually new.


Reproducibility isn't a claim — it's a receipt

build.py produces a deterministic zipapp: fixed timestamps, fixed permissions, normalized metadata, sorted archive contents, then hashes the result. Here's an actual run, captured twice, from deps-proof.txt:

Build 1 SHA-256: 276c1dc98360db0d61295258a3f6ee4073b7148c36155636c90cea91dc21401
Build 2 SHA-256: 276c1dc98360db0d61295258a3f6ee4073b7148c36155636c90cea91dc21401

MATCH: yes
Enter fullscreen mode Exit fullscreen mode

Same input, same bytes, twice. That's a stronger sentence than "the build is reproducible" — it's a claim you can rerun yourself in thirty seconds.

The determinism guarantee doesn't stop at the build. A dedicated test suite (test_determinism.py) checks byte-identical JSON across repeated runs, stable file ordering, stable finding ordering, deterministic HTML output, and bounded health scores — because a repo scanner that gives you a different answer every time is worse than useless in a CI pipeline.


CI-ready, not just demo-ready

This is the part that turns a hackathon toy into something a real team could adopt tomorrow:

python3 codeintel.py doctor . --fail-on high
python3 codeintel.py security . --baseline security-baseline.json --fail-on medium
Enter fullscreen mode Exit fullscreen mode
Exit code Meaning
0 success
1 policy failure — a finding met/exceeded --fail-on
2 usage / argument error
3 filesystem / path error
4 internal error

Drop it into a pipeline, gate merges on health regressions, and it never phones home. --json for machines, --plain/--no-color for logs, --baseline so a team only sees genuinely new security findings instead of re-litigating accepted ones every run.


The edge case that ate an afternoon

The test cases that matter aren't clean files — they're the ones that break naive assumptions: Unicode filenames, binary content, NUL bytes, invalid encodings, huge files, deeply nested directories, symlink loops, permission errors, empty repos, malformed baselines.

The one that actually cost real time was a symlink loop:

repo/
  src/
  link -> .
Enter fullscreen mode Exit fullscreen mode

link points back at the repo root. A naive recursive walker — which is exactly what an early version of the scanner was — turns that into unbounded recursion: repo/link/link/link/link/... until Python hits its recursion limit or the process just hangs, depending on how the traversal was written. The failure mode is ugly precisely because it looks like the tool is doing real work — CPU pinned, no error, no output — right up until it isn't.

The fix wasn't "add a depth limit and call it done," because a depth limit just turns an infinite loop into a very slow, very wrong scan of the same directory a thousand times. The real fix was refusing to follow directory symlinks at all, which — as a side effect — also closes off a class of "read outside the intended tree" attacks for free. That one bug is the reason the traversal layer tracks visited directories instead of trusting the filesystem to behave.

It's now a permanent regression test, and it's the best argument in the whole project for why "the standard library gave us the primitives, not the product": os.walk will follow that symlink into the loop by default. Nothing warns you. You find out the hard way, once.


What we deliberately did not build

Scope discipline is part of the engineering, not a footnote:

  • Not a compiler or a full AST analyzer for every language
  • Not a CVE database
  • Not a code-coverage engine
  • Not a semantic vector-search system
  • Not a language server or cloud platform

Symbol detection and complexity are regex/heuristic, and the output always says "estimated" or "probable" — never asserts certainty. We'd rather ship:

"A fast, explainable first-pass signal."

than:

"This understands your entire codebase."

That honesty is documented up front in STDLIB.md and the README's own limitations section — not buried, not discovered by a disappointed judge halfway through the demo.


Single file, real structure

The entire runtime lives in one file — codeintel.py, the single-file bonus target — but it's organized into 24 clearly delimited sections (# == SECTION) from constants and data models through the terminal/HTML renderers to main(). One shared AnalysisReport, produced once per scan, feeds every renderer — CLI, JSON, HTML, and the local browser UI shown above.

A single-file constraint changes the packaging model. It does not require abandoning engineering structure.


The scoreboard

Project:            CodeIntel Doctor Zero
Track:               A — Developer Tools & CLI
Runtime:             Python 3.8+ (built/tested on 3.12)
Third-party deps:    0
Network requests:    0
Implementation:      single codeintel.py (~2,500 lines, 24 sections)
Automated tests:     87 passing
STDLIB substitutions: 17, documented with honest limitations
Reproducible build:  verified — identical SHA-256 across two builds
Commands:            analyze · doctor · search · security · tree ·
                     duplicates · explain · report · ui · demo-color
Bonus targets hit:   Package Killer (terminal engine) · STDLIB Log ·
                     single-file · reproducible build · CI exit codes
Enter fullscreen mode Exit fullscreen mode

What zero dependencies actually taught us

Removing the package registry doesn't prove third-party packages are bad. It just forces you to see the layers sitting underneath them.

A table was a renderer we had to design. A duplicate was a definition we had to draw a line around. A health score was a model we had to make explainable. A security alert was a confidence threshold we had to choose and defend. Once the packages disappeared, every one of those decisions became ours to own — and to prove.

That's the actual submission for Zero Dependency Hack 2026: not "look, no packages," but a working, tested, reproducible developer tool where every design decision that would normally hide inside a pip install is visible, documented, and honest about its own limits.


Repo: https://github.com/rajpriyanid-creator/codeintel-doctor
Demo: https://youtu.be/W4I1-tRH7sE
Built for: Zero Dependency Hack 2026 — Track A, Developer Tools & CLI · Side Quest, The Write-Up

Tagging @HackathonRaptors — thanks for running Side Quest and rewarding the write-up over the vote count.


Tags: hackathonraptors· cli · opensource · showdev · hackathon · zerodependency · python

Top comments (2)

Collapse
 
karthika_8c92ca2117d08657 profile image
Karthika

Very Good one

Collapse
 
sasi_sanjay_9fff84a9df184 profile image
Sasi Sanjay

Bestt!!!