crapkit 0.4.11__tar.gz → 0.4.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {crapkit-0.4.11/src/crapkit.egg-info → crapkit-0.4.13}/PKG-INFO +23 -17
- {crapkit-0.4.11 → crapkit-0.4.13}/README.md +22 -16
- {crapkit-0.4.11 → crapkit-0.4.13}/pyproject.toml +1 -1
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/__init__.py +1 -1
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/__init__.py +26 -0
- crapkit-0.4.13/src/crapkit/cli/_shared.py +228 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/admin.py +269 -33
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/analyses.py +7 -3
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/parser.py +78 -4
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/queue.py +11 -9
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/ratchet_cmds.py +8 -6
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/reports.py +12 -10
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/scoring.py +7 -5
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/verifying.py +55 -16
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/config.py +56 -3
- crapkit-0.4.13/src/crapkit/coverage_py.py +164 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/covstream.py +34 -30
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/doctor.py +16 -8
- crapkit-0.4.13/src/crapkit/invocation.py +45 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/lanes.py +265 -26
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/mcp_server.py +100 -21
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/mutate_pool.py +57 -3
- crapkit-0.4.13/src/crapkit/procs.py +137 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/ratchet.py +32 -5
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/report.py +7 -2
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/scaffold.py +288 -24
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/uncovered.py +3 -1
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/universe.py +10 -1
- {crapkit-0.4.11 → crapkit-0.4.13/src/crapkit.egg-info}/PKG-INFO +23 -17
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit.egg-info/SOURCES.txt +1 -0
- crapkit-0.4.11/src/crapkit/cli/_shared.py +0 -130
- crapkit-0.4.11/src/crapkit/coverage_py.py +0 -87
- crapkit-0.4.11/src/crapkit/procs.py +0 -66
- {crapkit-0.4.11 → crapkit-0.4.13}/LICENSE +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/setup.cfg +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/__main__.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/_pygdefer.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/analyze.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cache.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/churn.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/churn_cache.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/churn_log.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/cli/claude_hook.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/coupling.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/coupling_cache.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/coverage_istanbul.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/diffparse.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/digest.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/discover.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/dup.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/errors.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/gitio.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/hook.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/junitparse.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/keys.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/lizardcognitive.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/lizardpowershell.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/lizardrust.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/lizardshell.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/merge.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/mutate.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/override.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/packet.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/ratchet_report.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/sarif.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/sarifio.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/score.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/snapshot.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/store.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/verify.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/watch.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit/worklist.py +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit.egg-info/dependency_links.txt +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit.egg-info/entry_points.txt +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit.egg-info/requires.txt +0 -0
- {crapkit-0.4.11 → crapkit-0.4.13}/src/crapkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crapkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.13
|
|
4
4
|
Summary: Scores every function on complexity times uncovered risk, ranks the worst, and blocks commits that add more.
|
|
5
5
|
Author: Jean-Francois Gagne
|
|
6
6
|
License: MIT
|
|
@@ -43,6 +43,7 @@ Dynamic: license-file
|
|
|
43
43
|
[](https://pypi.org/project/crapkit/)
|
|
44
44
|
[](https://pypi.org/project/crapkit/)
|
|
45
45
|
[](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
|
|
46
|
+
[](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit)
|
|
46
47
|
|
|
47
48
|

|
|
48
49
|
|
|
@@ -117,7 +118,9 @@ command when `pytest_cov` is missing; `pip install "crapkit[py]"` pulls the plug
|
|
|
117
118
|
alongside crapkit when the two share a venv. On a Windows PATH holding only the `py`
|
|
118
119
|
launcher it writes `py`, not a `python3` the lane could never run, and when cmd.exe cannot
|
|
119
120
|
start the interpreter at all (exit 9009, the Store alias) it names that instead of guessing
|
|
120
|
-
at pytest-cov.
|
|
121
|
+
at pytest-cov. A repo that pins no lockfile and carries its own `.venv` gets that venv's
|
|
122
|
+
interpreter in the lane, when that interpreter can import pytest, rather than whichever
|
|
123
|
+
python the shell answers with. The two quickstarts below walk a real repo end to end.
|
|
121
124
|
|
|
122
125
|
**On Windows a lane command is read by cmd.exe**, the shell that will run it, not by sh.
|
|
123
126
|
Double quotes are the portable quoting. A single-quoted value is refused at config load
|
|
@@ -129,7 +132,7 @@ artifact:
|
|
|
129
132
|
command = "python -m pytest -m 'not live and not perf' --cov=calc --cov-branch --cov-report=json:.crapkit/cov/py.json"
|
|
130
133
|
|
|
131
134
|
$ crapkit doctor
|
|
132
|
-
crapkit: lane 'py': positional argument 'live' narrows a full-suite coverage run; drop it, attach it to the flag it belongs to (-n8, --numprocesses=8), or set full_suite = false deliberately (cmd.exe does not treat ' as a quote: write the value in double quotes)
|
|
135
|
+
crapkit: lane 'py': positional argument 'live' narrows a full-suite coverage run; drop it, attach it to the flag it belongs to (-n8, --numprocesses=8), or set full_suite = false deliberately (cmd.exe does not treat ' as a quote: write the value in double quotes); a suite whose testpaths cannot be collected in one process needs one lane per testpath, each with full_suite = false and its own artifact
|
|
133
136
|
```
|
|
134
137
|
|
|
135
138
|
Write it `-m "not live and not perf"`. Carets, `&&` and `|` segments, redirections and
|
|
@@ -163,7 +166,7 @@ leaves the box
|
|
|
163
166
|
|
|
164
167
|
```
|
|
165
168
|
$ crapkit --version
|
|
166
|
-
crapkit 0.4.
|
|
169
|
+
crapkit 0.4.13
|
|
167
170
|
```
|
|
168
171
|
|
|
169
172
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -393,7 +396,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
393
396
|
repos:
|
|
394
397
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
395
398
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
396
|
-
rev: v0.4.
|
|
399
|
+
rev: v0.4.13
|
|
397
400
|
hooks:
|
|
398
401
|
- id: crapkit-gate
|
|
399
402
|
```
|
|
@@ -499,7 +502,7 @@ to a workflow, and every input has a default:
|
|
|
499
502
|
- uses: actions/checkout@v4
|
|
500
503
|
with:
|
|
501
504
|
fetch-depth: 0
|
|
502
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
505
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.13
|
|
503
506
|
```
|
|
504
507
|
|
|
505
508
|
The whole job those four lines sit in:
|
|
@@ -519,7 +522,7 @@ jobs:
|
|
|
519
522
|
with:
|
|
520
523
|
python-version: "3.12" # the interpreter the install below lands in
|
|
521
524
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
522
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
525
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.13
|
|
523
526
|
with:
|
|
524
527
|
gate: "false"
|
|
525
528
|
```
|
|
@@ -663,7 +666,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
663
666
|
| Command | What it does |
|
|
664
667
|
|---|---|
|
|
665
668
|
| `init` | Sniffs tracked source into per-directory scopes, writes a self-validated starter `crapkit.toml` whose lanes report into `.crapkit/cov/`, and appends `.crapkit/` plus each runner's own droppings to `.gitignore`. Writes a live `[[lane]]` when it can detect the test runner, otherwise a commented template. Refuses to clobber an existing config. |
|
|
666
|
-
| `doctor [--show-files] [--json] [--tune] [--plugin-root [PATH]]` | Checks the config still describes the repo: unknown keys (with the accepted spellings), zero-file scopes, tracked source no scope claims, scopes no lane covers, lane cwds and commands that no longer resolve, lizard importable, oversized files. It reads each lane command with the shell that will run it, so a quoted interpreter path is one word and a runner after `&&` is checked too, and it FAILs a lane whose runner does not resolve
|
|
669
|
+
| `doctor [--show-files] [--json] [--tune] [--plugin-root [PATH]]` | Checks the config still describes the repo: unknown keys (with the accepted spellings), zero-file scopes, tracked source no scope claims, scopes no lane covers, lane cwds and commands that no longer resolve, lizard importable, oversized files. It reads each lane command with the shell that will run it, so a quoted interpreter path is one word and a runner after `&&` is checked too, and it FAILs a lane whose runner does not resolve or that the shell cannot start, naming the word to change; a bare name is looked for on PATH and a runner spelled as a path is looked for under the directory the lane runs in, so `.venv/bin/python` answers the same from any directory you run `doctor` in; each distinct runner is probed once, not once per lane. It WARNs on a lane writing its artifact at the repo root, a `coveragepy` or `istanbul` lane with no `results_artifact` (the crashed-worker and no-new-failures checks are off for it, whichever runner the lane spells), a committed hook under `core.hooksPath` that is not executable in the index, a directory whose functions are all `untested` while its tests exist, and a scope a lane measures with no `[crapkit.scoped_tests]` template behind it, which is the loop's step 4 with nothing to run. `--tune` prints suggested parallelism knobs and writes nothing. `--plugin-root PATH` reads no repo at all: it checks an installed [plugin](https://github.com/JeanFrancoisGagne/crapkit/tree/main/plugin) against the `crapkit` on PATH (the bare name its hooks and MCP server spawn) on both version and hook `--protocol`, and FAILs when PATH carries no `crapkit` at all, one line per disagreement and silence when they agree; PATH is the plugin root or any directory above it, `~/.claude` included (only manifests named `crapkit` count, and the newest install wins), and with no PATH it looks in Claude Code's plugin cache. A root it found rather than one you typed is named first, as `crapkit doctor: checking PATH`. See [docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#doctor---json). |
|
|
667
670
|
| `inventory [--db PATH] [--export PATH] [--json]` | One lizard pass over every in-scope file into a SQLite snapshot run, cached by content hash. `--db` is the only way to point crapkit at a store outside `.crapkit/`, and only this command accepts it. |
|
|
668
671
|
| `coverage [--lane NAME] [--reuse-artifacts] [--reuse-unchanged] [--export PATH] [--sarif PATH] [--github] [--json]` | Runs the lanes, joins branch coverage onto a fresh inventory, writes a scored run. A failed lane is recorded, not fatal: its scopes fall back to `no-lane` and the run is typed `partial`, so it can never serve as a baseline. See [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md). |
|
|
669
672
|
| `verify [--baseline ID \| --base REF \| --baseline-tsv PATH] [--emit-baseline PATH] [--override REASON] [--reuse-artifacts] [--reuse-unchanged] [--no-tighten] [--sarif PATH] [--github] [--json]` | The full verdict against the trusted baseline: gate on touched functions, ratchet, no new test failures, optional diff-coverage ceiling. The three baseline selectors are mutually exclusive; `--baseline ID` also bypasses the taint rule ([The trusted baseline](#the-trusted-baseline)), and `--baseline-tsv` reads a commit-stamped file so a fresh clone verifies with no store. `--no-tighten` passes the verdict without rewriting the ratchet. Findings a dirty tree produced are tagged `dirty` and counted apart. It reads each istanbul artifact once for coverage, dead lines and its digest, and skips the artifact walk on an empty diff; skipping the whole run on an unchanged tree was measured and rejected, because a key made of HEAD plus the dirty names cannot see a second edit to a file that was already dirty. |
|
|
@@ -678,7 +681,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
678
681
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
679
682
|
| `trend [--json]` | Totals per trusted run: functions, over-target count, CRAP load, average, per-scope rollup. It reads a per-run rollup table rather than rescanning every scored row, and fills that table for any run missing one, so it writes to the store (best effort: a read-only `.crapkit/` costs the speed, not the command). |
|
|
680
683
|
| `digest [--alert]` | The delta between the two newest runs with identical lane sets. Silent when nothing changed. `--alert` pipes the body to `alert_command` on stdin. Plain lines, never JSON. |
|
|
681
|
-
| `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative), with the path printed on stdout. It renders what `worklist --json` and `trend --json` already answer at their defaults: the ranked worklist capped at `worklist_top`, the per-scope grades off the newest run, the trend series, and a banner naming every stale lane. It measures nothing and opens no network connection. Per-function CRAP and coverage are absent because no repo-wide payload carries them; each row prints the `crapkit explain` call that does. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
684
|
+
| `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative, or an absolute path you name), with the path printed on stdout. It renders what `worklist --json` and `trend --json` already answer at their defaults: the ranked worklist capped at `worklist_top`, the per-scope grades off the newest run, the trend series, and a banner naming every stale lane. It measures nothing and opens no network connection. Per-function CRAP and coverage are absent because no repo-wide payload carries them; each row prints the `crapkit explain` call that does. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
682
685
|
| `duplication [--min-lines N] [--similarity F] [--top N] [--json]` | Near-duplicate functions by normalized line shingles with containment scoring. Defaults: `--min-lines 8`, `--similarity 0.8`, `--top 50`. `--top` truncates the list. A function and a function nested inside it never pair: their spans nest, they score 1.0 by construction, and nobody can deduplicate a factory from its own closure. |
|
|
683
686
|
| `coupling [--min-support N] [--min-confidence F] [--top N] [--json]` | File pairs that keep landing in the same commits. Defaults: `--min-support 5` shared commits, `--min-confidence 0.5` max-direction ratio, `--top 50`. Bulk commits never couple pairs, and a young repo returns nothing at the default support. The ranked pairs are cached in `.crapkit/coupling-cache-v1.json`, keyed on HEAD, the churn window, today's UTC date, the path format and a digest of the tracked set, and shared with `brief` and `worklist --batches` (warm: 1.05 s to 0.11 s on a 72k-commit repo). The date is part of that key, so the first run after midnight UTC rebuilds the pairs on an unchanged HEAD. `--top` reads the cache, because it truncates that same order; `--min-support` or `--min-confidence` off their defaults ask a wider question than the file answers, so they bypass it and recompute. |
|
|
684
687
|
| `mutate [--files F ...] [--max-mutants N] [--drop-pool] [--json]` | Diff-scoped mutation testing: flips comparisons, boundary shifts, boolean connectives and boolean literals on changed lines, runs `mutation_command` per mutant, lists survivors. `--files` replaces diff scope with the whole file. `--max-mutants` (default 100) caps the run and the cap warning goes to stderr only, so `mutants` in `--json` is the capped count. Shell and PowerShell files are refused by name on stderr rather than mutated: `<` and `>` are redirections there, not comparisons. With `mutation_workers > 1` the worker worktrees are kept at `.crapkit/mutate-pool/` and re-prepared per run (30.6 s to build four on a 31,459-file repo, 0.46 s to re-prepare them); `--drop-pool` removes them and exits. |
|
|
@@ -686,6 +689,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
686
689
|
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
687
690
|
| `claude-hook [--protocol N]` | Reads one Claude Code PostToolUse payload from stdin and judges the file it edited: ccn against the scope ceiling, on functions the edit changed, minus functions a ratchet mark already covers. Advisory only: the edit has landed, and `hook-precommit` stays the enforcement point. Exit 2 and an advisory on stderr is the only thing it ever says, one block per judged file (a head line, one line per breaching function, a closing line): no `crapkit.toml` above the edited file, an unscoped file, mid-rebase or mid-merge, a `--protocol` other than 1, source that parses to no functions, or any internal failure all exit 0 in silence. The root is the first `crapkit.toml` above the edited file; the walk stops at a `.git` entry, so a worktree never borrows its parent's config. A `Bash` event names no file, so it judges the working tree instead: the dirty or untracked `*.py` files touched in the last 12 seconds, 25 at most, each through the same ladder, and silence for a clean tree or a cwd outside any repo. That half fires only where you register a `Bash` matcher ([The Claude Code plugin](#the-claude-code-plugin)). It opens no snapshot and writes nothing. |
|
|
688
691
|
| `watch [--interval SECONDS] [--cycles N]` | Rescores tracked files as they change (mtime polling, default 2s, subprocess-isolated so a half-saved syntax error never kills the watcher). `--cycles N` polls exactly N times and exits 0; without it the loop runs until ctrl-c. |
|
|
692
|
+
| `help [TOPIC]` | The help git, npm and docker answer to. With no TOPIC it prints the command list; with one it prints that subcommand's own help, the same page as `crapkit TOPIC --help`. A TOPIC that names no subcommand exits 3. |
|
|
689
693
|
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing nine read-only tools. Every tool shells to the CLI's own `--json` surface, so the MCP view cannot drift from what the CLI reports. Answering from a kept in-process store was benchmarked and rejected: a packet's `source` would go stale behind the edit it describes. See [docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#mcp-server). |
|
|
690
694
|
|
|
691
695
|
## Reading the output
|
|
@@ -882,11 +886,11 @@ paths = ["calc"]
|
|
|
882
886
|
languages = ["python"]
|
|
883
887
|
|
|
884
888
|
[exclude]
|
|
885
|
-
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
889
|
+
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "node_modules/**", "dist/**", "build/**", "vendor/**", "*.test.*", "*.spec.*", "test_*.py", "*_test.py", "conftest.py", "*_test.go", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
886
890
|
|
|
887
891
|
[[lane]]
|
|
888
892
|
name = "py"
|
|
889
|
-
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml"
|
|
893
|
+
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml --continue-on-collection-errors"
|
|
890
894
|
artifact = ".crapkit/cov/py.json"
|
|
891
895
|
results_artifact = ".crapkit/cov/junit-py.xml"
|
|
892
896
|
parser = "coveragepy"
|
|
@@ -895,7 +899,7 @@ scopes = ["calc"]
|
|
|
895
899
|
# Declare one [[lane]] per coverage command, then run `crapkit coverage`.
|
|
896
900
|
# [[lane]]
|
|
897
901
|
# name = "js"
|
|
898
|
-
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
902
|
+
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
899
903
|
# artifact = ".crapkit/cov/js/coverage-final.json"
|
|
900
904
|
# results_artifact = ".crapkit/cov/js/junit.xml"
|
|
901
905
|
# parser = "istanbul"
|
|
@@ -1010,7 +1014,7 @@ added to .gitignore: .crapkit/
|
|
|
1010
1014
|
```
|
|
1011
1015
|
|
|
1012
1016
|
The lane `init` wrote is
|
|
1013
|
-
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
1017
|
+
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
1014
1018
|
It reads vitest's `json` reporter from `.crapkit/cov/js/coverage-final.json`; the
|
|
1015
1019
|
`reportsDirectory` flag is what keeps that report out of your root. The junit half is the
|
|
1016
1020
|
lane's `results_artifact`, which the crashed-worker and no-new-failures checks read; both
|
|
@@ -1029,7 +1033,7 @@ exit 5:
|
|
|
1029
1033
|
|
|
1030
1034
|
```
|
|
1031
1035
|
$ crapkit coverage
|
|
1032
|
-
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
1036
|
+
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
1033
1037
|
|
|
1034
1038
|
MISSING DEPENDENCY Cannot find dependency '@vitest/coverage-v8'
|
|
1035
1039
|
|
|
@@ -1057,9 +1061,11 @@ The artifact crapkit wants is `coverage-final.json`, written by vitest's `json`
|
|
|
1057
1061
|
reporter, which is on by default. If your vitest config sets `coverage.reporter`
|
|
1058
1062
|
explicitly, keep `"json"` in the list.
|
|
1059
1063
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1064
|
+
vitest writes **no coverage report at all when the run fails**. The lane `init` wrote
|
|
1065
|
+
already carries `--coverage.reportOnFailure`, so a red test still produces the artifact.
|
|
1066
|
+
If you write the lane by hand, or you would rather keep the switch beside your other
|
|
1067
|
+
coverage settings, `coverage.reportOnFailure = true` in the vitest config does the same
|
|
1068
|
+
job; either one is enough. The full block is in
|
|
1063
1069
|
[docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#reportonfailure).
|
|
1064
1070
|
|
|
1065
1071
|
### 3. Score the repo
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/crapkit/)
|
|
5
5
|
[](https://pypi.org/project/crapkit/)
|
|
6
6
|
[](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
|
|
7
|
+
[](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit)
|
|
7
8
|
|
|
8
9
|

|
|
9
10
|
|
|
@@ -78,7 +79,9 @@ command when `pytest_cov` is missing; `pip install "crapkit[py]"` pulls the plug
|
|
|
78
79
|
alongside crapkit when the two share a venv. On a Windows PATH holding only the `py`
|
|
79
80
|
launcher it writes `py`, not a `python3` the lane could never run, and when cmd.exe cannot
|
|
80
81
|
start the interpreter at all (exit 9009, the Store alias) it names that instead of guessing
|
|
81
|
-
at pytest-cov.
|
|
82
|
+
at pytest-cov. A repo that pins no lockfile and carries its own `.venv` gets that venv's
|
|
83
|
+
interpreter in the lane, when that interpreter can import pytest, rather than whichever
|
|
84
|
+
python the shell answers with. The two quickstarts below walk a real repo end to end.
|
|
82
85
|
|
|
83
86
|
**On Windows a lane command is read by cmd.exe**, the shell that will run it, not by sh.
|
|
84
87
|
Double quotes are the portable quoting. A single-quoted value is refused at config load
|
|
@@ -90,7 +93,7 @@ artifact:
|
|
|
90
93
|
command = "python -m pytest -m 'not live and not perf' --cov=calc --cov-branch --cov-report=json:.crapkit/cov/py.json"
|
|
91
94
|
|
|
92
95
|
$ crapkit doctor
|
|
93
|
-
crapkit: lane 'py': positional argument 'live' narrows a full-suite coverage run; drop it, attach it to the flag it belongs to (-n8, --numprocesses=8), or set full_suite = false deliberately (cmd.exe does not treat ' as a quote: write the value in double quotes)
|
|
96
|
+
crapkit: lane 'py': positional argument 'live' narrows a full-suite coverage run; drop it, attach it to the flag it belongs to (-n8, --numprocesses=8), or set full_suite = false deliberately (cmd.exe does not treat ' as a quote: write the value in double quotes); a suite whose testpaths cannot be collected in one process needs one lane per testpath, each with full_suite = false and its own artifact
|
|
94
97
|
```
|
|
95
98
|
|
|
96
99
|
Write it `-m "not live and not perf"`. Carets, `&&` and `|` segments, redirections and
|
|
@@ -124,7 +127,7 @@ leaves the box
|
|
|
124
127
|
|
|
125
128
|
```
|
|
126
129
|
$ crapkit --version
|
|
127
|
-
crapkit 0.4.
|
|
130
|
+
crapkit 0.4.13
|
|
128
131
|
```
|
|
129
132
|
|
|
130
133
|
`python -m crapkit` works identically to the console script and is what to use from a
|
|
@@ -354,7 +357,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
|
|
|
354
357
|
repos:
|
|
355
358
|
- repo: https://github.com/JeanFrancoisGagne/crapkit
|
|
356
359
|
# crapkit's release step rewrites this line to the tag it just cut
|
|
357
|
-
rev: v0.4.
|
|
360
|
+
rev: v0.4.13
|
|
358
361
|
hooks:
|
|
359
362
|
- id: crapkit-gate
|
|
360
363
|
```
|
|
@@ -460,7 +463,7 @@ to a workflow, and every input has a default:
|
|
|
460
463
|
- uses: actions/checkout@v4
|
|
461
464
|
with:
|
|
462
465
|
fetch-depth: 0
|
|
463
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
466
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.13
|
|
464
467
|
```
|
|
465
468
|
|
|
466
469
|
The whole job those four lines sit in:
|
|
@@ -480,7 +483,7 @@ jobs:
|
|
|
480
483
|
with:
|
|
481
484
|
python-version: "3.12" # the interpreter the install below lands in
|
|
482
485
|
- run: pip install -e ".[dev]" # whatever your lanes need to run
|
|
483
|
-
- uses: JeanFrancoisGagne/crapkit@v0.4.
|
|
486
|
+
- uses: JeanFrancoisGagne/crapkit@v0.4.13
|
|
484
487
|
with:
|
|
485
488
|
gate: "false"
|
|
486
489
|
```
|
|
@@ -624,7 +627,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
624
627
|
| Command | What it does |
|
|
625
628
|
|---|---|
|
|
626
629
|
| `init` | Sniffs tracked source into per-directory scopes, writes a self-validated starter `crapkit.toml` whose lanes report into `.crapkit/cov/`, and appends `.crapkit/` plus each runner's own droppings to `.gitignore`. Writes a live `[[lane]]` when it can detect the test runner, otherwise a commented template. Refuses to clobber an existing config. |
|
|
627
|
-
| `doctor [--show-files] [--json] [--tune] [--plugin-root [PATH]]` | Checks the config still describes the repo: unknown keys (with the accepted spellings), zero-file scopes, tracked source no scope claims, scopes no lane covers, lane cwds and commands that no longer resolve, lizard importable, oversized files. It reads each lane command with the shell that will run it, so a quoted interpreter path is one word and a runner after `&&` is checked too, and it FAILs a lane whose runner does not resolve
|
|
630
|
+
| `doctor [--show-files] [--json] [--tune] [--plugin-root [PATH]]` | Checks the config still describes the repo: unknown keys (with the accepted spellings), zero-file scopes, tracked source no scope claims, scopes no lane covers, lane cwds and commands that no longer resolve, lizard importable, oversized files. It reads each lane command with the shell that will run it, so a quoted interpreter path is one word and a runner after `&&` is checked too, and it FAILs a lane whose runner does not resolve or that the shell cannot start, naming the word to change; a bare name is looked for on PATH and a runner spelled as a path is looked for under the directory the lane runs in, so `.venv/bin/python` answers the same from any directory you run `doctor` in; each distinct runner is probed once, not once per lane. It WARNs on a lane writing its artifact at the repo root, a `coveragepy` or `istanbul` lane with no `results_artifact` (the crashed-worker and no-new-failures checks are off for it, whichever runner the lane spells), a committed hook under `core.hooksPath` that is not executable in the index, a directory whose functions are all `untested` while its tests exist, and a scope a lane measures with no `[crapkit.scoped_tests]` template behind it, which is the loop's step 4 with nothing to run. `--tune` prints suggested parallelism knobs and writes nothing. `--plugin-root PATH` reads no repo at all: it checks an installed [plugin](https://github.com/JeanFrancoisGagne/crapkit/tree/main/plugin) against the `crapkit` on PATH (the bare name its hooks and MCP server spawn) on both version and hook `--protocol`, and FAILs when PATH carries no `crapkit` at all, one line per disagreement and silence when they agree; PATH is the plugin root or any directory above it, `~/.claude` included (only manifests named `crapkit` count, and the newest install wins), and with no PATH it looks in Claude Code's plugin cache. A root it found rather than one you typed is named first, as `crapkit doctor: checking PATH`. See [docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#doctor---json). |
|
|
628
631
|
| `inventory [--db PATH] [--export PATH] [--json]` | One lizard pass over every in-scope file into a SQLite snapshot run, cached by content hash. `--db` is the only way to point crapkit at a store outside `.crapkit/`, and only this command accepts it. |
|
|
629
632
|
| `coverage [--lane NAME] [--reuse-artifacts] [--reuse-unchanged] [--export PATH] [--sarif PATH] [--github] [--json]` | Runs the lanes, joins branch coverage onto a fresh inventory, writes a scored run. A failed lane is recorded, not fatal: its scopes fall back to `no-lane` and the run is typed `partial`, so it can never serve as a baseline. See [docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md). |
|
|
630
633
|
| `verify [--baseline ID \| --base REF \| --baseline-tsv PATH] [--emit-baseline PATH] [--override REASON] [--reuse-artifacts] [--reuse-unchanged] [--no-tighten] [--sarif PATH] [--github] [--json]` | The full verdict against the trusted baseline: gate on touched functions, ratchet, no new test failures, optional diff-coverage ceiling. The three baseline selectors are mutually exclusive; `--baseline ID` also bypasses the taint rule ([The trusted baseline](#the-trusted-baseline)), and `--baseline-tsv` reads a commit-stamped file so a fresh clone verifies with no store. `--no-tighten` passes the verdict without rewriting the ratchet. Findings a dirty tree produced are tagged `dirty` and counted apart. It reads each istanbul artifact once for coverage, dead lines and its digest, and skips the artifact walk on an empty diff; skipping the whole run on an unchanged tree was measured and rejected, because a key made of HEAD plus the dirty names cannot see a second edit to a file that was already dirty. |
|
|
@@ -639,7 +642,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
639
642
|
| `overrides [--json]` | The override audit trail: who granted what, when, and why. |
|
|
640
643
|
| `trend [--json]` | Totals per trusted run: functions, over-target count, CRAP load, average, per-scope rollup. It reads a per-run rollup table rather than rescanning every scored row, and fills that table for any run missing one, so it writes to the store (best effort: a read-only `.crapkit/` costs the speed, not the command). |
|
|
641
644
|
| `digest [--alert]` | The delta between the two newest runs with identical lane sets. Silent when nothing changed. `--alert` pipes the body to `alert_command` on stdin. Plain lines, never JSON. |
|
|
642
|
-
| `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative), with the path printed on stdout. It renders what `worklist --json` and `trend --json` already answer at their defaults: the ranked worklist capped at `worklist_top`, the per-scope grades off the newest run, the trend series, and a banner naming every stale lane. It measures nothing and opens no network connection. Per-function CRAP and coverage are absent because no repo-wide payload carries them; each row prints the `crapkit explain` call that does. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
645
|
+
| `report [--out PATH]` | One self-contained HTML page written to `.crapkit/report.html` (or `--out PATH`, repo-relative, or an absolute path you name), with the path printed on stdout. It renders what `worklist --json` and `trend --json` already answer at their defaults: the ranked worklist capped at `worklist_top`, the per-scope grades off the newest run, the trend series, and a banner naming every stale lane. It measures nothing and opens no network connection. Per-function CRAP and coverage are absent because no repo-wide payload carries them; each row prints the `crapkit explain` call that does. It reads the same per-run rollups `trend` does, and writes them on the same terms. |
|
|
643
646
|
| `duplication [--min-lines N] [--similarity F] [--top N] [--json]` | Near-duplicate functions by normalized line shingles with containment scoring. Defaults: `--min-lines 8`, `--similarity 0.8`, `--top 50`. `--top` truncates the list. A function and a function nested inside it never pair: their spans nest, they score 1.0 by construction, and nobody can deduplicate a factory from its own closure. |
|
|
644
647
|
| `coupling [--min-support N] [--min-confidence F] [--top N] [--json]` | File pairs that keep landing in the same commits. Defaults: `--min-support 5` shared commits, `--min-confidence 0.5` max-direction ratio, `--top 50`. Bulk commits never couple pairs, and a young repo returns nothing at the default support. The ranked pairs are cached in `.crapkit/coupling-cache-v1.json`, keyed on HEAD, the churn window, today's UTC date, the path format and a digest of the tracked set, and shared with `brief` and `worklist --batches` (warm: 1.05 s to 0.11 s on a 72k-commit repo). The date is part of that key, so the first run after midnight UTC rebuilds the pairs on an unchanged HEAD. `--top` reads the cache, because it truncates that same order; `--min-support` or `--min-confidence` off their defaults ask a wider question than the file answers, so they bypass it and recompute. |
|
|
645
648
|
| `mutate [--files F ...] [--max-mutants N] [--drop-pool] [--json]` | Diff-scoped mutation testing: flips comparisons, boundary shifts, boolean connectives and boolean literals on changed lines, runs `mutation_command` per mutant, lists survivors. `--files` replaces diff scope with the whole file. `--max-mutants` (default 100) caps the run and the cap warning goes to stderr only, so `mutants` in `--json` is the capped count. Shell and PowerShell files are refused by name on stderr rather than mutated: `<` and `>` are redirections there, not comparisons. With `mutation_workers > 1` the worker worktrees are kept at `.crapkit/mutate-pool/` and re-prepared per run (30.6 s to build four on a 31,459-file repo, 0.46 s to re-prepare them); `--drop-pool` removes them and exits. |
|
|
@@ -647,6 +650,7 @@ crapkit: error: argument command: invalid choice: '/path/to/repo' (choose from '
|
|
|
647
650
|
| `hook-precommit` | The cc-only gate on staged blobs. No coverage, no snapshot, no repo-wide cache. Exit 6 on a violation. |
|
|
648
651
|
| `claude-hook [--protocol N]` | Reads one Claude Code PostToolUse payload from stdin and judges the file it edited: ccn against the scope ceiling, on functions the edit changed, minus functions a ratchet mark already covers. Advisory only: the edit has landed, and `hook-precommit` stays the enforcement point. Exit 2 and an advisory on stderr is the only thing it ever says, one block per judged file (a head line, one line per breaching function, a closing line): no `crapkit.toml` above the edited file, an unscoped file, mid-rebase or mid-merge, a `--protocol` other than 1, source that parses to no functions, or any internal failure all exit 0 in silence. The root is the first `crapkit.toml` above the edited file; the walk stops at a `.git` entry, so a worktree never borrows its parent's config. A `Bash` event names no file, so it judges the working tree instead: the dirty or untracked `*.py` files touched in the last 12 seconds, 25 at most, each through the same ladder, and silence for a clean tree or a cwd outside any repo. That half fires only where you register a `Bash` matcher ([The Claude Code plugin](#the-claude-code-plugin)). It opens no snapshot and writes nothing. |
|
|
649
652
|
| `watch [--interval SECONDS] [--cycles N]` | Rescores tracked files as they change (mtime polling, default 2s, subprocess-isolated so a half-saved syntax error never kills the watcher). `--cycles N` polls exactly N times and exits 0; without it the loop runs until ctrl-c. |
|
|
653
|
+
| `help [TOPIC]` | The help git, npm and docker answer to. With no TOPIC it prints the command list; with one it prints that subcommand's own help, the same page as `crapkit TOPIC --help`. A TOPIC that names no subcommand exits 3. |
|
|
650
654
|
| `mcp` | A dependency-free stdio MCP server (newline JSON-RPC 2.0) exposing nine read-only tools. Every tool shells to the CLI's own `--json` surface, so the MCP view cannot drift from what the CLI reports. Answering from a kept in-process store was benchmarked and rejected: a packet's `source` would go stale behind the edit it describes. See [docs/agent-json.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/agent-json.md#mcp-server). |
|
|
651
655
|
|
|
652
656
|
## Reading the output
|
|
@@ -843,11 +847,11 @@ paths = ["calc"]
|
|
|
843
847
|
languages = ["python"]
|
|
844
848
|
|
|
845
849
|
[exclude]
|
|
846
|
-
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
850
|
+
globs = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/vendor/**", "**/*.test.*", "**/*.spec.*", "**/test_*.py", "**/*_test.py", "**/conftest.py", "node_modules/**", "dist/**", "build/**", "vendor/**", "*.test.*", "*.spec.*", "test_*.py", "*_test.py", "conftest.py", "*_test.go", "**/*_test.go", "*.config.ts", "*.config.js", "*.config.mts", "**/*.config.ts", "**/*.config.js", "**/*.config.mts"]
|
|
847
851
|
|
|
848
852
|
[[lane]]
|
|
849
853
|
name = "py"
|
|
850
|
-
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml"
|
|
854
|
+
command = "python -m pytest --cov --cov-branch --cov-report=json:.crapkit/cov/py.json --junitxml=.crapkit/cov/junit-py.xml --continue-on-collection-errors"
|
|
851
855
|
artifact = ".crapkit/cov/py.json"
|
|
852
856
|
results_artifact = ".crapkit/cov/junit-py.xml"
|
|
853
857
|
parser = "coveragepy"
|
|
@@ -856,7 +860,7 @@ scopes = ["calc"]
|
|
|
856
860
|
# Declare one [[lane]] per coverage command, then run `crapkit coverage`.
|
|
857
861
|
# [[lane]]
|
|
858
862
|
# name = "js"
|
|
859
|
-
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
863
|
+
# command = "npx vitest run --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml"
|
|
860
864
|
# artifact = ".crapkit/cov/js/coverage-final.json"
|
|
861
865
|
# results_artifact = ".crapkit/cov/js/junit.xml"
|
|
862
866
|
# parser = "istanbul"
|
|
@@ -971,7 +975,7 @@ added to .gitignore: .crapkit/
|
|
|
971
975
|
```
|
|
972
976
|
|
|
973
977
|
The lane `init` wrote is
|
|
974
|
-
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
978
|
+
`npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml`.
|
|
975
979
|
It reads vitest's `json` reporter from `.crapkit/cov/js/coverage-final.json`; the
|
|
976
980
|
`reportsDirectory` flag is what keeps that report out of your root. The junit half is the
|
|
977
981
|
lane's `results_artifact`, which the crashed-worker and no-new-failures checks read; both
|
|
@@ -990,7 +994,7 @@ exit 5:
|
|
|
990
994
|
|
|
991
995
|
```
|
|
992
996
|
$ crapkit coverage
|
|
993
|
-
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
997
|
+
crapkit: lane 'js' FAILED: lane 'js' produced no artifact at .crapkit/cov/js/coverage-final.json (command exit 1); full log: /repo/.crapkit/lane-js.log; last output: $ npm run test -- --coverage --coverage.reportsDirectory=.crapkit/cov/js --coverage.reportOnFailure --reporter=default --reporter=junit --outputFile=.crapkit/cov/js/junit.xml
|
|
994
998
|
|
|
995
999
|
MISSING DEPENDENCY Cannot find dependency '@vitest/coverage-v8'
|
|
996
1000
|
|
|
@@ -1018,9 +1022,11 @@ The artifact crapkit wants is `coverage-final.json`, written by vitest's `json`
|
|
|
1018
1022
|
reporter, which is on by default. If your vitest config sets `coverage.reporter`
|
|
1019
1023
|
explicitly, keep `"json"` in the list.
|
|
1020
1024
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1025
|
+
vitest writes **no coverage report at all when the run fails**. The lane `init` wrote
|
|
1026
|
+
already carries `--coverage.reportOnFailure`, so a red test still produces the artifact.
|
|
1027
|
+
If you write the lane by hand, or you would rather keep the switch beside your other
|
|
1028
|
+
coverage settings, `coverage.reportOnFailure = true` in the vitest config does the same
|
|
1029
|
+
job; either one is enough. The full block is in
|
|
1024
1030
|
[docs/lanes.md](https://github.com/JeanFrancoisGagne/crapkit/blob/main/docs/lanes.md#reportonfailure).
|
|
1025
1031
|
|
|
1026
1032
|
### 3. Score the repo
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "crapkit"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.13"
|
|
8
8
|
description = "Scores every function on complexity times uncovered risk, ranks the worst, and blocks commits that add more."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""crapkit: deterministic CRAP-score framework."""
|
|
2
|
-
__version__ = "0.4.
|
|
2
|
+
__version__ = "0.4.13"
|
|
@@ -59,8 +59,10 @@ _OWNER = {
|
|
|
59
59
|
"_claimable": "queue",
|
|
60
60
|
"_claims_summary": "queue",
|
|
61
61
|
"_claims_to_release": "queue",
|
|
62
|
+
"_clearing_spellings": "verifying",
|
|
62
63
|
"_collect_lanes": "scoring",
|
|
63
64
|
"_collect_mutants": "analyses",
|
|
65
|
+
"_committed_launcher": "admin",
|
|
64
66
|
"_config": "claude_hook",
|
|
65
67
|
"_contexts_for_span": "reports",
|
|
66
68
|
"_coupling_pairs": "analyses",
|
|
@@ -118,16 +120,21 @@ _OWNER = {
|
|
|
118
120
|
"_guard_ratchet_stamp": "verifying",
|
|
119
121
|
"_handle": "queue",
|
|
120
122
|
"_held_marks": "verifying",
|
|
123
|
+
"_help_topic": "parser",
|
|
124
|
+
"_help_topics": "parser",
|
|
121
125
|
"_hook_handlers": "admin",
|
|
122
126
|
"_hook_modes": "admin",
|
|
123
127
|
"_hook_protocols": "admin",
|
|
128
|
+
"_imports_pytest": "admin",
|
|
124
129
|
"_installed_crapkit_roots": "admin",
|
|
125
130
|
"_interpreter": "admin",
|
|
131
|
+
"_is_rooted": "_shared",
|
|
126
132
|
"_is_test_path": "verifying",
|
|
127
133
|
"_judge": "claude_hook",
|
|
128
134
|
"_junit_seconds": "admin",
|
|
129
135
|
"_lane_command_problems": "admin",
|
|
130
136
|
"_lane_durations": "admin",
|
|
137
|
+
"_lane_path": "admin",
|
|
131
138
|
"_lane_problem": "admin",
|
|
132
139
|
"_lane_problems": "admin",
|
|
133
140
|
"_lane_report": "admin",
|
|
@@ -142,18 +149,22 @@ _OWNER = {
|
|
|
142
149
|
"_load_ratchet_or_die": "_shared",
|
|
143
150
|
"_load_repo_config": "_shared",
|
|
144
151
|
"_load_sources": "_shared",
|
|
152
|
+
"_looks_like_a_path": "parser",
|
|
145
153
|
"_manifest_field": "admin",
|
|
146
154
|
"_manifest_roots": "admin",
|
|
147
155
|
"_manifest_version": "admin",
|
|
148
156
|
"_mark_text": "queue",
|
|
157
|
+
"_marker_texts": "admin",
|
|
149
158
|
"_matching_rows": "queue",
|
|
150
159
|
"_maybe_claim": "queue",
|
|
151
160
|
"_marks_for": "claude_hook",
|
|
152
161
|
"_maybe_flake_retry": "verifying",
|
|
153
162
|
"_merge_stamp": "ratchet_cmds",
|
|
154
163
|
"_missing_named_script": "admin",
|
|
164
|
+
"_missing_runner": "admin",
|
|
155
165
|
"_more_skips_line": "verifying",
|
|
156
166
|
"_mutation_targets": "analyses",
|
|
167
|
+
"_name_found_root": "admin",
|
|
157
168
|
"_name_matches": "queue",
|
|
158
169
|
"_name_prefix": "queue",
|
|
159
170
|
"_named_baseline": "verifying",
|
|
@@ -168,6 +179,7 @@ _OWNER = {
|
|
|
168
179
|
"_next_reasons": "queue",
|
|
169
180
|
"_next_step": "admin",
|
|
170
181
|
"_no_baseline": "verifying",
|
|
182
|
+
"_no_crapkit_on_path": "admin",
|
|
171
183
|
"_no_handle_message": "queue",
|
|
172
184
|
"_no_lane_debt": "queue",
|
|
173
185
|
"_no_lane_gap": "queue",
|
|
@@ -185,12 +197,14 @@ _OWNER = {
|
|
|
185
197
|
"_packet_context": "queue",
|
|
186
198
|
"_packet_gate": "queue",
|
|
187
199
|
"_packet_scope": "queue",
|
|
200
|
+
"_path_first_arg": "parser",
|
|
188
201
|
"_payload": "claude_hook",
|
|
189
202
|
"_pick_baseline": "verifying",
|
|
190
203
|
"_pick_function": "queue",
|
|
191
204
|
"_plugin_json": "admin",
|
|
192
205
|
"_plugins_dir": "admin",
|
|
193
206
|
"_policy_findings": "ratchet_cmds",
|
|
207
|
+
"_positive_top": "_shared",
|
|
194
208
|
"_present_lockfiles": "admin",
|
|
195
209
|
"_present_markers": "admin",
|
|
196
210
|
"_present_on_disk": "scoring",
|
|
@@ -200,6 +214,7 @@ _OWNER = {
|
|
|
200
214
|
"_print_brief_context": "queue",
|
|
201
215
|
"_print_brief_neighbours": "queue",
|
|
202
216
|
"_print_claims": "queue",
|
|
217
|
+
"_print_clear_the_reason": "verifying",
|
|
203
218
|
"_print_coverage": "scoring",
|
|
204
219
|
"_print_duplication": "analyses",
|
|
205
220
|
"_print_finding_split": "verifying",
|
|
@@ -215,6 +230,7 @@ _OWNER = {
|
|
|
215
230
|
"_print_trend": "reports",
|
|
216
231
|
"_print_uncovered": "reports",
|
|
217
232
|
"_print_verify_findings": "verifying",
|
|
233
|
+
"_probed_cli_version": "admin",
|
|
218
234
|
"_progress": "scoring",
|
|
219
235
|
"_prune_renames": "ratchet_cmds",
|
|
220
236
|
"_pruned": "ratchet_cmds",
|
|
@@ -227,6 +243,7 @@ _OWNER = {
|
|
|
227
243
|
"_ratchet_mark": "reports",
|
|
228
244
|
"_ratchet_merge": "ratchet_cmds",
|
|
229
245
|
"_ratchet_move": "ratchet_cmds",
|
|
246
|
+
"_ratchet_or_die": "_shared",
|
|
230
247
|
"_ratchet_report": "ratchet_cmds",
|
|
231
248
|
"_ratchet_sha256": "verifying",
|
|
232
249
|
"_reconfigure_streams": "parser",
|
|
@@ -235,8 +252,12 @@ _OWNER = {
|
|
|
235
252
|
"_records_by_scope": "scoring",
|
|
236
253
|
"_refuse_empty_lane_run": "scoring",
|
|
237
254
|
"_refuse_lane_less_verify": "verifying",
|
|
255
|
+
"_refuse_path_argument": "parser",
|
|
238
256
|
"_release_claims": "verifying",
|
|
239
257
|
"_release_target": "queue",
|
|
258
|
+
"_repo_out_path": "_shared",
|
|
259
|
+
"_repo_relative": "_shared",
|
|
260
|
+
"_repo_venv_python": "admin",
|
|
240
261
|
"_report": "claude_hook",
|
|
241
262
|
"_report_lanes": "reports",
|
|
242
263
|
"_report_payload": "reports",
|
|
@@ -274,6 +295,7 @@ _OWNER = {
|
|
|
274
295
|
"_sequencing": "claude_hook",
|
|
275
296
|
"_settle_verify": "verifying",
|
|
276
297
|
"_skip_reason": "queue",
|
|
298
|
+
"_spawned_cli": "admin",
|
|
277
299
|
"_split_marked": "verifying",
|
|
278
300
|
"_split_worklist": "queue",
|
|
279
301
|
"_stale_warning": "queue",
|
|
@@ -292,12 +314,15 @@ _OWNER = {
|
|
|
292
314
|
"_uncovered_fields": "queue",
|
|
293
315
|
"_uncovered_scopes": "admin",
|
|
294
316
|
"_uncovered_text": "reports",
|
|
317
|
+
"_under_root": "_shared",
|
|
295
318
|
"_under_scope_path": "verifying",
|
|
296
319
|
"_unknown_key_text": "admin",
|
|
297
320
|
"_unmarked_breaches": "scoring",
|
|
298
321
|
"_unknown_claude_command": "parser",
|
|
299
322
|
"_untracked_of": "scoring",
|
|
300
323
|
"_untrusted_reason": "verifying",
|
|
324
|
+
"_venv_dirs": "admin",
|
|
325
|
+
"_venv_launcher": "admin",
|
|
301
326
|
"_verdict": "claude_hook",
|
|
302
327
|
"_verify_attribution": "verifying",
|
|
303
328
|
"_verify_baseline": "verifying",
|
|
@@ -335,6 +360,7 @@ _OWNER = {
|
|
|
335
360
|
"cmd_doctor": "admin",
|
|
336
361
|
"cmd_duplication": "analyses",
|
|
337
362
|
"cmd_explain": "reports",
|
|
363
|
+
"cmd_help": "parser",
|
|
338
364
|
"cmd_hook_precommit": "verifying",
|
|
339
365
|
"cmd_init": "admin",
|
|
340
366
|
"cmd_inventory": "scoring",
|