Tells you what to be afraid of before you touch a file.
Project description
WhyCode
Tells you what to be afraid of before touching a file.
git blame answers "who". git log answers "what". WhyCode answers the
question your senior engineer asks before any change:
"Should I be careful here?"
It mines what your repository already remembers — reverts, hotfixes, incident-tagged commits, ghost authors, tightly coupled files, long silences, verbatim warnings from past authors — and condenses them into a single Risk Card. Then it hands that card to you, your CI, or the AI that's about to edit the code.
Who is this for?
WhyCode is most valuable in moments when the wrong edit hurts. If you recognise yourself in any of these, it'll pay rent:
- Solo dev returning to a 3-month-old side project —
whycode why <file>to remember why the weird bit is weird before the AI "fixes" it. - Senior engineer joining an unfamiliar codebase —
whycode scanto map the load-bearing walls;whycode whybefore each first edit. - AI-paired developer — install the MCP server and your editor's assistant can read invariants and incident history before it refactors.
- Tech lead reviewing PRs —
whycode diffranks the PR's files by risk so attention goes to the scary changes first. - CI / pre-commit gating —
whycode diff --fail-on historyblocks the build when high-risk files change without explanation. - SRE doing a 3am hotfix —
whycode why <file> --brieffor a one-line "what should I be afraid of?" sanity check. - Junior dev wanting to ship without breaking things —
whycode scanshows where to step lightly.
What WhyCode is not for: replacing git blame, telling you what
changed, or suggesting fixes. It tells you the why and the risk. You
decide what to do.
Install
pip install whycode-cli
The PyPI distribution is whycode-cli (the bare name whycode was already
taken by an unrelated project); the installed command is still whycode.
From source, if you want to track main:
pip install git+https://github.com/fangshuor/WhyCode.git
Requires Python 3.11+.
60-second tour
cd /path/to/your/repo
whycode tour # the one command to run first
whycode init # one-command setup: CI workflow + pre-commit gate
whycode highlights # repo-wide treasure map: top decisions + incidents
whycode why src/some/file.py # the Risk Card for one file
whycode why src/some/file.py -b # one-line summary (for triage / scripts)
whycode why src/some/file.py --at <sha> # risk as of a past commit
whycode why src/some/file.py --mute <kind> # local "this signal is wrong, hide it" feedback
whycode diff # rank everything you changed vs origin/main
whycode diff --staged # ditto, for files staged for commit
whycode diff --fail-on history # CI gate: exit 1 if any file is ≥ READ HISTORY FIRST
whycode show <sha> # classification + per-file risk for one commit
whycode timeline src/some/file.py # risk score evolution across the file's history
whycode honest src/some/file.py # every invariant line, verbatim, untruncated
whycode scan --top 10 # the riskiest files in the whole repo
whycode mcp -v # MCP server with tool-call logging
That's it. No config file, no daemon, no account, no upload.
What a Risk Card looks like
╭─ READ HISTORY FIRST score 57/100 ──────────────────────────────╮
│ src/payment/refund.py (24 commits) │
│ Latest: hotfix: idempotency token regression │
│ a3f4b2c1 Mei Chen 2025-09-14 │
╰──────────────────────────────────────────────────────────────────╯
HIGH 3 reverts touched this file
9d2e7a1 reverts c5b81fe; bbf441c reverts 4d29ab0; …
MED 2 incident-flagged changes in history
2 commits matched incident keywords (latest 12 days ago:
'hotfix: idempotency token regression').
evidence: a3f4b2c, 7e22a04
MED 2 invariants stated by past authors
> Do not switch to async — v1 clients break. (4d29ab0)
> Important: keep the legacy header in place. (c5b81fe)
→ git show 9d2e7a1 to read the most relevant commit in full
Score interpretation:
| Score | Band | What to do |
|---|---|---|
| 75–100 | HANDLE WITH CARE | Stop. Read the linked commits first. |
| 50–74 | READ HISTORY FIRST | At least skim the top signal. |
| 25–49 | WORTH A LOOK | One thing might bite you. Glance. |
| 0–24 | NO FLAGS | Quiet history — but read the diff anyway. |
"But why exactly did this fire?" — --explain
When a signal looks wrong (or you just want to understand the reasoning
before trusting the tool), pass --explain. Each fired signal grows a
small block naming the precise rule that produced it, the literal evidence
the rule looked at, and the source location of the ladder branch:
$ whycode why src/payment/refund.py --explain
MED 1 incident-flagged change in history
1 commit matched incident keywords (latest 12 days ago:
'hotfix: idempotency token regression').
evidence: a3f4b2c
─ rule: incident_subject_keyword src/whycode/git_facts.py:find_incidents
fired because: subject 'hotfix: idempotency token regression'
matched the literal token 'hotfix'
evidence: hotfix
Without --explain, output is exactly as before — this is purely an
opt-in transparency surface. --explain --json adds an explanation
key per signal in the JSON output, with the same fields. The flag covers
L1+L2 detectors only; if you also pass --llm, the L3 decision block is
unaffected.
The killer use case: hand it to your AI editor
WhyCode is also an MCP server. Configure it in any MCP-aware editor or assistant, and the host LLM can pull a Risk Card before it edits any file.
{
"mcpServers": {
"whycode": { "command": "whycode", "args": ["mcp"] }
}
}
Drop that snippet into your editor's MCP configuration file (location varies by editor — check your editor's MCP docs). Then in any chat:
"Refactor the refund flow."
A well-configured assistant will call get_risk_profile("src/payment/refund.py")
first and read the warnings before it changes a line. Run whycode mcp -v
during development to log every tool call to stderr so you can verify the
integration is actually live.
Tools exposed:
get_risk_profile(path)— full Risk Card.get_file_decisions(path, limit=5)— only the decision-flavoured signals (reverts, incidents, ghost keepers, invariant quotes).
Wire it into git, CI, and your editor
WhyCode is most useful when it shows up automatically in the moments you'd otherwise forget to look. The fast path:
whycode init
That installs two things:
.git/hooks/pre-commit— runswhycode diff --staged --fail-on handlebefore every commit. HANDLE WITH CARE files can't be touched without an explicitgit commit --no-verify..github/workflows/whycode.yml— a GitHub Action that prints a risk-ranked table for every PR. Advisory by default (never blocks merging); append--fail-on history(orhandle/look) to the diff line to turn it into a hard gate.
Tune the thresholds inside those two files for your repo. Re-run with
whycode init --force to overwrite.
MCP server — see the next section.
Architecture (three layers, by design)
| Layer | What | Network? | API key? |
|---|---|---|---|
| 1 | Deterministic git facts (log, diffstat, revert pairs, author activity) | no | no |
| 2 | Heuristic signals (reverts, incidents, silence, ghost keeper, coupling, invariants, churn, newborn) | no | no |
| 3 | LLM-extracted structured decisions (optional, opt-in, never on by default) | yes | yes |
Layer 1 + Layer 2 produce the Risk Card by default. No model calls, no
data leaving your machine. Layer 3 lifts the keyword fragments L1 + L2
extract ("do not switch to async") into structured decisions with the
why drawn from the surrounding commit body — but only when you ask for
it with --llm.
Optional L3 — LLM-enriched decisions
Install the optional extras and configure the env vars:
pip install 'whycode-cli[llm]'
export WHYCODE_LLM_API_KEY="…"
export WHYCODE_LLM_MODEL="<your-provider's-model-identifier>"
whycode why src/some/file.py --llm # full card + structured decisions
whycode why src/some/file.py --llm-dry-run # see exactly what would be sent
Privacy contract: configuration is entirely environment-driven (no hardcoded provider in the source tree); the SDK is lazy-imported (no import cost unless you opt in); only L2-filtered high-signal commits are sent (capped at 10 per call); a malformed model response degrades to "no decisions" rather than crashing.
What this is NOT
- ❌ Not a SaaS. No accounts, no cloud, no telemetry.
- ❌ Not a code review bot. WhyCode reports — never prescribes.
- ❌ Not a "what changed" tool. Plenty of those exist already.
- ❌ Not language-specific. We read git history, not your AST.
- ❌ Not a replacement for
git blame. It's the briefing your senior would give you before you opened blame.
Developing
git clone https://github.com/fangshuor/WhyCode.git
cd WhyCode
pip install -e '.[dev,mcp]'
ruff check . && mypy src/whycode/ && pytest -q
See ENGINEERING.md for the engineering charter — the
durable rules this repo is built under.
License
MIT. © 2026 Kevin.
Project details
Release history Release notifications | RSS feed
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 whycode_cli-0.5.2.tar.gz.
File metadata
- Download URL: whycode_cli-0.5.2.tar.gz
- Upload date:
- Size: 96.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2a61288a4cb6e6f65b3a8cb5aab1728f4327dc40c71d64c8c8ee1aab69d224a
|
|
| MD5 |
01e68fe72ce61f181d200ad01bb556a8
|
|
| BLAKE2b-256 |
13df9f37b88cc03629d6b6daf6d27af67015b6f5d0465e86688749d8efe1c590
|
File details
Details for the file whycode_cli-0.5.2-py3-none-any.whl.
File metadata
- Download URL: whycode_cli-0.5.2-py3-none-any.whl
- Upload date:
- Size: 72.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bcc356a06c239dddc9bea571a81cf37b18e27f9fc4e8090aca12f166d7305efe
|
|
| MD5 |
40eef9ff64a947bbcd1640866e968889
|
|
| BLAKE2b-256 |
22d0a47dccf12f0a3aff01e68422b8e1bc226216e97dd49642f3e78072b3b3c9
|