v0.11.1 · Agent-output calibration · release notes
·
open source · MIT · local · no cloud
A crime scene investigator for your codebase.
Built for agents, readable by humans.
crimes is an open-source CLI that scans a TypeScript or
JavaScript repository for change risk and
agent risk — duplicated business rules, ambiguous
sources of truth, weakly tested hotspots, and patterns that confuse
AI coding agents. Local, deterministic, no cloud.
$ npx crimes scan .
CRIME SCENE REPORT
repo: messy-ts-app · 5 findings
HIGH severity (1)
1. src/billing.ts:37-240 (generateInvoice)
Charge: God Function
Evidence: 204 lines · 3.4× threshold · function declaration
id=crime_00001 confidence=0.95
Total 5 · high 1 medium 3 low 1
Install
crimes is published on
npm and requires
Node.js 18 or newer.
npm install -g crimes
crimes scan .
Or one-shot via npx:
npx crimes scan .
pnpm dlx crimes scan and bunx crimes scan
also work. Homebrew lands after npm — see the
roadmap.
Quick start
Nine commands cover everything crimes does today:
Scan a directory
crimes scan .
Discovers TS/JS files, runs every detector, prints the top 10
findings ranked by severity. Pass --all for the
full list.
JSON output (the product contract)
crimes scan . --format json
Versioned via schema_version (currently
"0.2.0"). Every report carries a
report_type discriminator so consumers can route
on one field. Every Finding ships
effort + fix_shape alongside the
existing scores.
Pre-edit briefing for a single file
crimes context src/billing/tax.ts --format json
Returns the file's findings, the test files likely to cover it,
and short safe-editing notes. The cheapest, most file-specific
entry point for an agent.
Working-tree-only scan (with optional CI gate)
crimes scan --changed --format json
crimes scan --changed --base main --format json
crimes scan --changed --fail-on high
Restricts to files changed in the working tree, optionally
including commits unique to the current branch.
--fail-on low|medium|high (only valid with
--changed) exits 1 when a finding
in the changed set meets the threshold — the cheapest CI
gate.
Branch-level diff between two refs
crimes diff main...HEAD --format json
crimes diff origin/main...HEAD --format json
New, fixed, and
unchanged crimes between two Git refs.
Working-tree-safe — exports each ref into a temp dir via
git archive. Findings are matched by stable
fingerprint, so unrelated line shifts don't register as
fix + new.
Baseline — pin legacy debt, gate on new
crimes baseline save
git add .crimes/baseline.json && git commit -m "Add crimes baseline"
crimes baseline check --fail-on medium
Snapshot the current findings to
.crimes/baseline.json, commit the file, then
fail CI only on findings absent from the baseline.
--fail-on defaults to medium;
exit 1 blocks CI, exit 2 is
reserved for missing / malformed baselines.
Branch verdict — cleaner, worse, unchanged, or mixed
crimes verdict --base origin/main --format json
crimes verdict --base origin/main --fail-on new-high
One-line "did this branch help or hurt?" summary, built on
crimes diff. Default base picks
origin/main first, then main.
Advisory by default; opt into a hard gate with
--fail-on worse | new-high | new-medium.
Triage — the front door for existing findings
crimes triage
crimes triage --list
crimes triage --retriage src/billing.ts
Interactive per-finding walk, top-of-rank first. Each
prompt records a disposition
(fix-now, fix-this-PR,
needs-design, wont-fix,
scaffolding), a one-line reason, and an
owner. Silenced dispositions and baseline entries
resurface in crimes scan
when the file is in the branch diff against
main — a one-time decision doesn't become
permanent amnesia. Persisted to
.crimes/triage.json, intended to be
committed.
Churn × findings hotspots
crimes hotspots --since 90d --format json
Ranks files by an aggregate score of git churn and current
findings. Use it to triage where to look first.
Built for agents
The wedge isn't "better linter". It's local, open-source,
agent-native codebase risk and context. crimes
ships two on-disk artefacts that AI coding agents pick up
automatically — there is nothing to install into a
prompt.
Claude Code
.claude/skills/crimes/SKILL.md
A Claude Code skill that loads on demand. Tells Claude
when to run crimes, which
command to use, and how to read the JSON.
Auto-discovered by Claude Code in any repo with a
SKILL.md under
.claude/skills/<name>/. As of
0.11.0, crimes init --agents
also installs a Claude Code
PreToolUse Edit hook in
.claude/settings.local.json so the pre-edit
briefing runs automatically — no prompt required.
- Pre-edit (automatic via PreToolUse):
crimes context <file> --format json
- Triage existing findings:
crimes triage
- Post-edit:
crimes scan --changed --format json
- New
severity: "high" after your edit → blocker
Codex · Cursor · Aider · more
AGENTS.md
The convention OpenAI Codex CLI, Cursor, Aider, Continue, and
Copilot Workspace read on startup. A single file at the repo
root with install commands, the shipped CLI surface, package
boundaries, and agent safety rules — no auto-publish, no
silent auto-fix.
- Stable JSON makes
crimes output safe to feed back into the agent loop
- Same pre / post-edit workflow as Claude
- Works with any agent that reads
AGENTS.md
Recommended loop
-
Before editing a specific file —
crimes context <file> --format json.
Read every high severity finding first. Under
Claude Code with the 0.11.1 PreToolUse hook
installed, this fires automatically on every
Edit / Write /
NotebookEdit.
-
Triage findings interactively —
crimes triage walks each finding top-of-rank
first, asking for a disposition
(fix-now / fix-this-PR /
needs-design / wont-fix /
scaffolding), one-line reason, and owner.
Silenced entries resurface in scan when the
file is touched on the branch.
-
After editing — re-scan only what you touched:
crimes scan --changed --format json. Diff the
findings against the pre-edit run.
-
Before opening the PR —
crimes verdict --base origin/main --fail-on new-high
for the one-line "did this branch help or hurt?" answer,
with a hard gate on any new high finding.
-
Triaging the wider repo —
crimes hotspots --format json ranks files by an
aggregate of git churn and current findings.
Decision rule: any new severity: "high"
finding introduced by your edit is a blocker — fix it, or
surface it to the user citing the finding id and
charge. Quote evidence[] back to the
user when you explain a decision: it is deterministic AST or
file-content data, not opinion.
CI
crimes is built for CI. Three deterministic gating
modes — pick whichever fits the contract you want with your
team. All three share the same exit-code shape:
0 success / no blocking findings,
1 a configured --fail-on threshold
met, 2 a usage or environment error.
Mode
Command
When to use
A · Changed-files
crimes scan --changed --fail-on high
Repo already clean, or you only want to gate the diff
itself.
B · Baseline
crimes baseline check --fail-on medium
Legacy repo — commit .crimes/baseline.json,
then fail only on new debt.
C · Branch verdict
crimes verdict --base origin/main --fail-on new-high
PR summary that flips to a hard gate on any new high
finding.
GitHub Actions — copy / paste
name: crimes
on:
pull_request:
push:
branches: [main]
jobs:
crimes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install -g crimes
- run: crimes verdict --base origin/main --fail-on new-high
Full integration guide and Mode A / B swaps:
docs/ci.md. Copy-paste workflow:
examples/github-actions/crimes.yml.
Example output
Human report
CRIME SCENE REPORT
repo: messy-ts-app · 5 findings
HIGH severity (1)
1. src/billing.ts:37-240 (generateInvoice)
Charge: God Function
Summary: generateInvoice spans 204 lines — past the 60-line
threshold for a single function. Bodies this size usually mix
unrelated responsibilities, and an agent editing one section
often misses interactions in another.
Evidence:
· lines 37–240 (204 lines)
· 3.4× the configured 60-line threshold
· function declaration
id=crime_00001 confidence=0.95
MEDIUM severity (3)
1. src/billing.ts:44-260
Charge: Temporal Recklessness
Summary: 7 direct uses of Date.now()/new Date(). Reading the
system clock in domain code makes behaviour non-deterministic
and couples tests to wall time.
...
Total 5 · high 1 medium 3 low 1
JSON report (the contract)
{
"schema_version": "0.2.0",
"report_type": "scan",
"repo": { "name": "messy-ts-app", "root": "/..." },
"summary": { "total": 5, "high": 1, "medium": 3, "low": 1 },
"findings": [
{
"id": "crime_00001",
"type": "large_function",
"charge": "God Function",
"severity": "high",
"confidence": 0.95,
"file": "src/billing.ts",
"symbol": "generateInvoice",
"lines": [37, 240],
"summary": "generateInvoice spans 204 lines — past the …",
"evidence": [
"lines 37–240 (204 lines)",
"3.4× the configured 60-line threshold",
"function declaration"
],
"effort": "medium",
"fix_shape": "extract orchestration; move pure helpers to a sibling module",
"scores": {
"severity": 0.9,
"confidence": 0.95,
"agent_risk": 0.95
},
"suggested_actions": [
{
"kind": "extract_function",
"description": "Extract cohesive sections into named helpers …",
"risk": "low"
}
]
}
]
}
schema_version is bumped on breaking changes, so
agents can refuse to consume an unfamiliar shape. The
"0.1.0" → "0.2.0" bump in
0.11.0 added two required Finding
fields: effort
(quick/small/medium/large)
and fix_shape (a one-line description of the shape
of the fix). Three docs back this contract up:
What it detects today
Structural detectors · shipped in v0.1.0
Detector
Charge
What it flags
large_function
God Function
Functions / methods / arrows past a body-line threshold
(default 60). Escalates to high at 2× threshold.
large_file
God File
Files past a line threshold (default 300). Same severity ramp.
todo_density
Unfinished Business
Files dense with TODO / FIXME /
XXX / HACK markers, with line ranges
for each cluster.
direct_date
Temporal Recklessness
Direct Date.now() / new Date() calls
in source files, with per-call line numbers.
Information architecture detectors · new in v0.3.0
IA crimes surface deterministic evidence that
the repo tells multiple competing stories about the same
product concept — what something is called, where it lives,
which implementation owns it. No LLM, no API key, no network.
Every finding cites concrete files, lines, and string literals;
cross-file findings populate related_files and
render as an "Also touches:" block in the human report.
Detector
Charge
What it flags
missing_agent_context
Missing Agent Context
Repos that declare a bin in
package.json but ship no AGENTS.md,
no CLAUDE.md, and no
.claude/skills/*/SKILL.md.
route_metadata_drift
Route Metadata Drift
Route path, file location, default-export component,
<title>, metadata.title, and
nav labels describe the same destination with competing
concept tokens (≥3-source quorum).
duplicated_navigation_source
Duplicated Navigation Source
A single internal destination (e.g.
/settings/billing) appears in two or more
nav-like sources with different non-empty labels.
concept_alias_drift
Concept Alias Drift
Multiple aliases from a seeded concept group
(team / workspace /
org; plan /
subscription / tier) appear across
the product surface, each in ≥2 distinct directories.
docs_code_drift
Docs-Code Drift
A markdown doc under docs/ (or a root-level
*.md) contains a local link that does not
resolve to a file on disk.
Example IA findings (from the bundled fixture)
alias group: tenant
aliases found: account, organisation, team, workspace
"account" in 2 file(s): docs/billing.md, src/routes/account/subscription.tsx
"team" in 2 file(s): docs/teams.md, src/routes/team/index.tsx
"workspace" in 2 file(s): docs/teams.md, src/routes/workspace/members.tsx
route path: /settings/billing
file: src/routes/settings/billing.tsx
component: PricingPage
metadata.title: Plans
<title>: Subscription
nav label in src/nav/registry.ts: Plans
destination: /settings/billing
src/nav/registry.ts label: Plans
src/nav/sidebar.ts label: Billing
no AGENTS.md found at repo root
no CLAUDE.md found at repo root
no .claude/skills/*/SKILL.md present
package.json declares bin(s): messy — agents have no way to discover commands
docs/teams.md:5 → ./setup.md (not found)
Built for humans and coding agents. IA findings phrase summaries
as "appears to" / "may" — they are
ambiguity signals, not claims of semantic truth.
Long-form reference (quorum rules, false-positive notes,
suggested fixes):
docs/finding-types/ia.md.
Every finding includes evidence (raw facts the
detector observed) and scores
(severity, confidence,
agent_risk). No verdicts without receipts.
FAQ
Short, direct answers to the questions that come up most often.
Every answer is also encoded as
FAQPage
structured data, so AI assistants can quote them verbatim.
What is crimes?
crimes is an open-source CLI that scans a
TypeScript or JavaScript repository for change
risk and AI-agent risk — duplicated
business rules, ambiguous sources of truth, weakly tested
hotspots, and patterns that confuse AI coding agents. It runs
locally with no cloud calls and outputs both a human report
and a stable, versioned JSON contract.
How is crimes different from ESLint, Biome,
Semgrep, or SonarQube?
crimes is not a linter, a security scanner, or a
quality platform. ESLint and Biome catch style and
correctness; Semgrep and CodeQL catch security issues;
SonarQube wraps both with a dashboard. crimes
ranks change-risk and agent-risk — where future edits are
most likely to go wrong, and what an AI coding agent needs to
know before editing a file. It is deliberately scoped to
that wedge and does not re-implement other tools.
Does crimes send my code to a server or LLM?
No. crimes runs entirely on your machine. There
are no accounts, no telemetry, and no SaaS dashboard. Core
detectors are deterministic AST and file-content analysis.
Any future LLM-assisted features will be optional, additive,
and opt-in — never load-bearing.
Which languages does crimes support?
The first language pack is TypeScript and JavaScript,
including JSX and TSX. The detector core and finding schema
are language-agnostic, so additional language packs can be
added without breaking the JSON contract. Python and other
languages are tracked as future work.
How do AI coding agents use crimes?
Agents read two on-disk artefacts that ship in the repo.
Claude Code picks up
.claude/skills/crimes/SKILL.md automatically;
Codex CLI, Cursor, Aider, Continue, and Copilot Workspace
read AGENTS.md at the repo root. The recommended
loop is: run
crimes context <file> --format json before
editing, then crimes scan --changed --format json
after editing, and treat any new
severity: "high" finding as a blocker.
Why is the JSON output called the contract?
The JSON shape is the product, not an
afterthought. Every finding is a typed record with
id, type, charge,
severity, confidence,
evidence, scores, and
suggested_actions. The top-level
schema_version is bumped on any breaking change,
so an AI agent or CI system can refuse to consume an
unfamiliar shape rather than guess. The human terminal output
is a renderer over the same schema.
Is crimes free?
Yes. crimes is
MIT-licensed
and published on
npm
as the unscoped package crimes. There is no paid
tier, no hosted service, and no telemetry. Install with
npm install -g crimes or run one-shot with
npx crimes scan .
What's next
Active development. Per-version release notes — every detector,
schema change, and command added so far — live on the
GitHub Releases page.
The forward roadmap is in
docs/roadmap.md; the authoritative product spec is
PRD.md.
Local, open-source, no cloud
crimes is
MIT-licensed
and runs entirely on your machine. No accounts, no telemetry, no
SaaS dashboard. Core detectors are deterministic; any
LLM-assisted features in the future are optional and additive.
Detectors live in packages/core, language parsing in
packages/language-js, reporters in
packages/reporter. Adding a detector is a pull
request, not a plugin marketplace.