crimes

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

Not another linter. Not a security scanner. crimes answers: where is future change most likely to go wrong, and what should a human or agent know before editing it?

Install

crimes is published on npm and requires Node.js 18 or newer.

# Global install
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

  1. 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.
  2. Triage findings interactivelycrimes 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.
  3. After editing — re-scan only what you touched: crimes scan --changed --format json. Diff the findings against the pre-edit run.
  4. Before opening the PRcrimes 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.
  5. Triaging the wider repocrimes 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

# .github/workflows/crimes.yml
name: crimes
on:
  pull_request:
  push:
    branches: [main]

jobs:
  crimes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          # `verdict` resolves `origin/main` — fetch full history.
          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)

# Concept Alias Drift
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 Metadata Drift
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

# Duplicated Navigation Source
destination: /settings/billing
src/nav/registry.ts label: Plans
src/nav/sidebar.ts label: Billing

# Missing Agent Context
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-Code Drift
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.