crapkit 0.4.12__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.
Files changed (73) hide show
  1. {crapkit-0.4.12/src/crapkit.egg-info → crapkit-0.4.13}/PKG-INFO +6 -5
  2. {crapkit-0.4.12 → crapkit-0.4.13}/README.md +5 -4
  3. {crapkit-0.4.12 → crapkit-0.4.13}/pyproject.toml +1 -1
  4. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/__init__.py +1 -1
  5. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/mcp_server.py +97 -20
  6. {crapkit-0.4.12 → crapkit-0.4.13/src/crapkit.egg-info}/PKG-INFO +6 -5
  7. {crapkit-0.4.12 → crapkit-0.4.13}/LICENSE +0 -0
  8. {crapkit-0.4.12 → crapkit-0.4.13}/setup.cfg +0 -0
  9. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/__main__.py +0 -0
  10. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/_pygdefer.py +0 -0
  11. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/analyze.py +0 -0
  12. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cache.py +0 -0
  13. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/churn.py +0 -0
  14. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/churn_cache.py +0 -0
  15. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/churn_log.py +0 -0
  16. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/__init__.py +0 -0
  17. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/_shared.py +0 -0
  18. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/admin.py +0 -0
  19. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/analyses.py +0 -0
  20. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/claude_hook.py +0 -0
  21. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/parser.py +0 -0
  22. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/queue.py +0 -0
  23. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/ratchet_cmds.py +0 -0
  24. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/reports.py +0 -0
  25. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/scoring.py +0 -0
  26. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/cli/verifying.py +0 -0
  27. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/config.py +0 -0
  28. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/coupling.py +0 -0
  29. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/coupling_cache.py +0 -0
  30. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/coverage_istanbul.py +0 -0
  31. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/coverage_py.py +0 -0
  32. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/covstream.py +0 -0
  33. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/diffparse.py +0 -0
  34. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/digest.py +0 -0
  35. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/discover.py +0 -0
  36. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/doctor.py +0 -0
  37. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/dup.py +0 -0
  38. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/errors.py +0 -0
  39. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/gitio.py +0 -0
  40. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/hook.py +0 -0
  41. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/invocation.py +0 -0
  42. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/junitparse.py +0 -0
  43. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/keys.py +0 -0
  44. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/lanes.py +0 -0
  45. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/lizardcognitive.py +0 -0
  46. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/lizardpowershell.py +0 -0
  47. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/lizardrust.py +0 -0
  48. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/lizardshell.py +0 -0
  49. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/merge.py +0 -0
  50. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/mutate.py +0 -0
  51. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/mutate_pool.py +0 -0
  52. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/override.py +0 -0
  53. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/packet.py +0 -0
  54. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/procs.py +0 -0
  55. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/ratchet.py +0 -0
  56. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/ratchet_report.py +0 -0
  57. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/report.py +0 -0
  58. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/sarif.py +0 -0
  59. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/sarifio.py +0 -0
  60. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/scaffold.py +0 -0
  61. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/score.py +0 -0
  62. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/snapshot.py +0 -0
  63. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/store.py +0 -0
  64. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/uncovered.py +0 -0
  65. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/universe.py +0 -0
  66. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/verify.py +0 -0
  67. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/watch.py +0 -0
  68. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit/worklist.py +0 -0
  69. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit.egg-info/SOURCES.txt +0 -0
  70. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit.egg-info/dependency_links.txt +0 -0
  71. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit.egg-info/entry_points.txt +0 -0
  72. {crapkit-0.4.12 → crapkit-0.4.13}/src/crapkit.egg-info/requires.txt +0 -0
  73. {crapkit-0.4.12 → 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.12
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
  [![PyPI](https://img.shields.io/pypi/v/crapkit)](https://pypi.org/project/crapkit/)
44
44
  [![Python](https://img.shields.io/pypi/pyversions/crapkit)](https://pypi.org/project/crapkit/)
45
45
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
46
+ [![crapkit MCP server](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit/badges/score.svg)](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit)
46
47
 
47
48
  ![crapkit init, coverage and worklist --top 5 on a small Python repo, then a shell heredoc adding a function at ccn 7: the per-edit advisory reports it and exits 2, and the commit gate refuses the staged file with exit 6](https://raw.githubusercontent.com/JeanFrancoisGagne/crapkit/main/docs/demo.gif)
48
49
 
@@ -165,7 +166,7 @@ leaves the box
165
166
 
166
167
  ```
167
168
  $ crapkit --version
168
- crapkit 0.4.12
169
+ crapkit 0.4.13
169
170
  ```
170
171
 
171
172
  `python -m crapkit` works identically to the console script and is what to use from a
@@ -395,7 +396,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
395
396
  repos:
396
397
  - repo: https://github.com/JeanFrancoisGagne/crapkit
397
398
  # crapkit's release step rewrites this line to the tag it just cut
398
- rev: v0.4.12
399
+ rev: v0.4.13
399
400
  hooks:
400
401
  - id: crapkit-gate
401
402
  ```
@@ -501,7 +502,7 @@ to a workflow, and every input has a default:
501
502
  - uses: actions/checkout@v4
502
503
  with:
503
504
  fetch-depth: 0
504
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
505
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
505
506
  ```
506
507
 
507
508
  The whole job those four lines sit in:
@@ -521,7 +522,7 @@ jobs:
521
522
  with:
522
523
  python-version: "3.12" # the interpreter the install below lands in
523
524
  - run: pip install -e ".[dev]" # whatever your lanes need to run
524
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
525
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
525
526
  with:
526
527
  gate: "false"
527
528
  ```
@@ -4,6 +4,7 @@
4
4
  [![PyPI](https://img.shields.io/pypi/v/crapkit)](https://pypi.org/project/crapkit/)
5
5
  [![Python](https://img.shields.io/pypi/pyversions/crapkit)](https://pypi.org/project/crapkit/)
6
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
7
+ [![crapkit MCP server](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit/badges/score.svg)](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit)
7
8
 
8
9
  ![crapkit init, coverage and worklist --top 5 on a small Python repo, then a shell heredoc adding a function at ccn 7: the per-edit advisory reports it and exits 2, and the commit gate refuses the staged file with exit 6](https://raw.githubusercontent.com/JeanFrancoisGagne/crapkit/main/docs/demo.gif)
9
10
 
@@ -126,7 +127,7 @@ leaves the box
126
127
 
127
128
  ```
128
129
  $ crapkit --version
129
- crapkit 0.4.12
130
+ crapkit 0.4.13
130
131
  ```
131
132
 
132
133
  `python -m crapkit` works identically to the console script and is what to use from a
@@ -356,7 +357,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
356
357
  repos:
357
358
  - repo: https://github.com/JeanFrancoisGagne/crapkit
358
359
  # crapkit's release step rewrites this line to the tag it just cut
359
- rev: v0.4.12
360
+ rev: v0.4.13
360
361
  hooks:
361
362
  - id: crapkit-gate
362
363
  ```
@@ -462,7 +463,7 @@ to a workflow, and every input has a default:
462
463
  - uses: actions/checkout@v4
463
464
  with:
464
465
  fetch-depth: 0
465
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
466
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
466
467
  ```
467
468
 
468
469
  The whole job those four lines sit in:
@@ -482,7 +483,7 @@ jobs:
482
483
  with:
483
484
  python-version: "3.12" # the interpreter the install below lands in
484
485
  - run: pip install -e ".[dev]" # whatever your lanes need to run
485
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
486
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
486
487
  with:
487
488
  gate: "false"
488
489
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "crapkit"
7
- version = "0.4.12"
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.12"
2
+ __version__ = "0.4.13"
@@ -13,9 +13,13 @@ from pathlib import Path
13
13
 
14
14
  from .invocation import _self
15
15
 
16
- PROTOCOL_VERSION = "2024-11-05"
16
+ # Newest first. Everything this server does — tools, annotations, structured
17
+ # results over stdio — is inside the 2025-06-18 revision, and nothing it does
18
+ # was removed from the older two, so any of the three can be spoken verbatim.
19
+ SUPPORTED_PROTOCOLS = ("2025-06-18", "2025-03-26", "2024-11-05")
17
20
 
18
- _REPO = {"repo": {"type": "string", "description": "repo root (default: the server's)"}}
21
+ _REPO = {"repo": {"type": "string", "description": (
22
+ "path to the scored repo's root (default: the repo the server was started in)")}}
19
23
 
20
24
  # brief and explain resolve NAME by one rule, so they describe it with one
21
25
  # string. The bare identifier is the long name's leading token, which is all
@@ -28,45 +32,80 @@ _NAME_DESCRIPTION = ("the bare identifier (classify, or route for a Rust "
28
32
  TOOLS: tuple[dict, ...] = (
29
33
  {"name": "next_item", "argv": ["next-item"], "json_flag": False, "positional": (),
30
34
  "flags": {"top": "--top", "exclude": "--exclude"},
31
- "description": "The highest-risk function(s) to refactor next, with scores and effort estimates",
32
- "properties": {"top": {"type": "integer"}, "exclude": {"type": "array", "items": {"type": "string"}}}},
35
+ "description": "The highest-risk function to refactor next, as one work packet: score, "
36
+ "uncovered lines, effort estimate and the commands that verify the fix. "
37
+ "Reach for it when you want one item to act on; worklist is the same "
38
+ "ranking as a survey.",
39
+ "properties": {"top": {"type": "integer",
40
+ "description": "return the next N packets instead of one (>= 1, default 1)"},
41
+ "exclude": {"type": "array", "items": {"type": "string"},
42
+ "description": "skip items whose path or function name contains "
43
+ "this fragment (repeatable)"}}},
33
44
  {"name": "worklist", "argv": ["worklist"], "json_flag": True, "positional": (),
34
45
  "flags": {"top": "--top"},
35
- "description": "Risk-ranked decomposition queue (ccn x recency-weighted churn)",
36
- "properties": {"top": {"type": "integer"}}},
46
+ "description": "The run's whole risk ranking: every function over its ceiling, then the "
47
+ "queue under it ordered by ccn times recency-weighted churn. Use it to "
48
+ "survey the repo; next_item hands out one packet from the top.",
49
+ "properties": {"top": {"type": "integer",
50
+ "description": "cap the active list (default: the config's worklist_top)"}}},
37
51
  {"name": "runs", "argv": ["runs"], "json_flag": True, "positional": (), "flags": {},
38
- "description": "Run history: id, kind, verdict, commit, lane set", "properties": {}},
52
+ "description": "Run history, newest first: id, kind, verdict, commit and lane set. Use it "
53
+ "to date the store or to check which commit the other tools answer from.",
54
+ "properties": {}},
39
55
  {"name": "brief", "argv": ["brief"], "json_flag": True, "positional": ("path", "name"),
40
56
  "flags": {},
41
- "description": "One function's whole context: scored row, ratchet mark, uncovered lines, "
42
- "duplication twins, churn and change-coupling partners",
57
+ "description": "One function's whole context in one call: scored row, ratchet mark, "
58
+ "uncovered lines, duplication twins, churn and change-coupling partners. "
59
+ "The deepest single read; explain is the slimmer history-only view.",
43
60
  "properties": {"path": {"type": "string", "description": "repo-relative source file"},
44
61
  "name": {"type": "string", "description": _NAME_DESCRIPTION}}},
45
62
  {"name": "explain", "argv": ["explain"], "json_flag": False, "positional": ("path", "name"),
46
63
  "flags": {},
47
- "description": "One function's score trajectory across runs plus its ratchet mark",
48
- "properties": {"path": {"type": "string"},
64
+ "description": "One function's score trajectory across runs plus its ratchet mark. Use it "
65
+ "to see whether a function is improving or decaying; brief adds the full "
66
+ "context around the newest score.",
67
+ "properties": {"path": {"type": "string", "description": "repo-relative source file"},
49
68
  "name": {"type": "string", "description": _NAME_DESCRIPTION}}},
50
69
  {"name": "doctor", "argv": ["doctor"], "json_flag": False, "positional": (), "flags": {},
51
- "description": "Config/repo agreement check: typo keys, empty scopes, missing lane cwds",
70
+ "description": "Config and repo agreement check: typo keys, empty scopes, missing lane "
71
+ "cwds, unresolvable runners. Run it first when any other tool answers "
72
+ "strangely or an expected file is missing from the ranking.",
52
73
  "properties": {}},
53
74
  {"name": "coupling", "argv": ["coupling"], "json_flag": True, "positional": (),
54
75
  "flags": {"min_support": "--min-support", "min_confidence": "--min-confidence"},
55
- "description": "File pairs that keep landing in the same commits (hidden dependencies)",
56
- "properties": {"min_support": {"type": "integer"}, "min_confidence": {"type": "number"}}},
76
+ "description": "File pairs that keep landing in the same commits: dependencies no import "
77
+ "statement reveals. Use it to learn what else an edit usually drags along "
78
+ "before touching a file.",
79
+ "properties": {"min_support": {"type": "integer",
80
+ "description": "minimum shared commits before a pair counts (default 5)"},
81
+ "min_confidence": {"type": "number",
82
+ "description": "minimum P(pair changes together), 0 to 1 (default 0.5)"}}},
57
83
  {"name": "duplication", "argv": ["duplication"], "json_flag": True, "positional": (),
58
84
  "flags": {"similarity": "--similarity"},
59
- "description": "Near-duplicate functions by normalized line shingles",
60
- "properties": {"similarity": {"type": "number"}}},
85
+ "description": "Near-duplicate function pairs by normalized line shingles, with the "
86
+ "containment percent. Use it before a refactor so twins get folded "
87
+ "together instead of one copy getting fixed alone.",
88
+ "properties": {"similarity": {"type": "number",
89
+ "description": "containment threshold, shared over smaller, "
90
+ "0 to 1 (default 0.8)"}}},
61
91
  {"name": "ratchet_report", "argv": ["ratchet", "report"], "json_flag": True, "positional": (),
62
92
  "flags": {},
63
- "description": "Debt burn-down: open mark ages and repayment velocity from git history",
93
+ "description": "The debt burn-down: every open ratchet mark with its age, plus repayment "
94
+ "velocity from git history. Use it to see whether marked debt is being "
95
+ "paid down or piling up.",
64
96
  "properties": {}},
65
97
  )
66
98
 
99
+ # Every tool shells to a read-only CLI command against a store on this machine:
100
+ # nothing writes, reruns answer the same, and no call leaves the repo. Declared
101
+ # once, in the field clients read, so the module docstring's promise reaches the
102
+ # model that plans with these tools.
103
+ _ANNOTATIONS = {"readOnlyHint": True, "idempotentHint": True, "openWorldHint": False}
104
+
67
105
 
68
106
  def tool_listing() -> list[dict]:
69
107
  return [{"name": t["name"], "description": t["description"],
108
+ "annotations": dict(_ANNOTATIONS),
70
109
  "inputSchema": {"type": "object", "properties": {**_REPO, **t["properties"]}}}
71
110
  for t in TOOLS]
72
111
 
@@ -116,7 +155,7 @@ def _run_cli(tool: dict, arguments: dict, repo: str) -> dict:
116
155
  proc = subprocess.run([sys.executable, "-m", "crapkit", *argv],
117
156
  capture_output=True, text=True, timeout=600)
118
157
  text = proc.stdout if proc.stdout.strip() else proc.stderr
119
- return _result(text, is_error=proc.returncode != 0)
158
+ return _structured(_result(text, is_error=proc.returncode != 0))
120
159
 
121
160
 
122
161
  def _call_tool(root: Path, name: str, arguments: dict) -> dict:
@@ -130,6 +169,43 @@ def _call_tool(root: Path, name: str, arguments: dict) -> dict:
130
169
  return _run_cli(tool, arguments, repo)
131
170
 
132
171
 
172
+ # What a connected model needs before its first call, in the one field the
173
+ # protocol reserves for it. The nine error results a model would otherwise
174
+ # collect from an unmeasured repo teach the same thing nine times, slower.
175
+ _INSTRUCTIONS = (
176
+ "crapkit scores every function as ccn^2 x (1 - coverage)^3 + ccn; these nine tools read "
177
+ "the newest scored run and every one is read-only: nothing here runs a test suite or "
178
+ "writes to the repo. They need a repo measured once (crapkit init, then crapkit "
179
+ "coverage); an unmeasured repo answers with a one-line pointer instead of data. Start "
180
+ "with next_item for one function to fix, worklist for the whole ranking, brief for "
181
+ "everything about one function.")
182
+
183
+
184
+ def _negotiated(params: dict) -> str:
185
+ """The client's revision when this server implements it, else the newest it
186
+ does; the spec leaves proceeding or disconnecting to the client from there."""
187
+ offered = params.get("protocolVersion")
188
+ return offered if offered in SUPPORTED_PROTOCOLS else SUPPORTED_PROTOCOLS[0]
189
+
190
+
191
+ def _structured(result: dict) -> dict:
192
+ """Attach the parsed object beside the text when the text is a JSON object.
193
+
194
+ The --json commands print for machines; a client on the 2025-06-18 revision
195
+ reads structuredContent directly. Prose, JSON arrays and error text stay
196
+ text-only rather than getting wrapped into shapes the tools never promised.
197
+ """
198
+ if result["isError"]:
199
+ return result
200
+ try:
201
+ parsed = json.loads(result["content"][0]["text"])
202
+ except ValueError:
203
+ return result
204
+ if isinstance(parsed, dict):
205
+ return {**result, "structuredContent": parsed}
206
+ return result
207
+
208
+
133
209
  def _respond(msg_id, result=None, error=None) -> dict:
134
210
  resp = {"jsonrpc": "2.0", "id": msg_id}
135
211
  resp["error" if error else "result"] = error if error else result
@@ -142,9 +218,10 @@ def _handle(root: Path, msg: dict) -> dict | None:
142
218
  return None # a notification (e.g. notifications/initialized) needs no reply
143
219
  if method == "initialize":
144
220
  return _respond(msg["id"], {
145
- "protocolVersion": PROTOCOL_VERSION,
221
+ "protocolVersion": _negotiated(msg.get("params", {})),
146
222
  "capabilities": {"tools": {}},
147
- "serverInfo": {"name": "crapkit", "version": _version()}})
223
+ "serverInfo": {"name": "crapkit", "version": _version()},
224
+ "instructions": _INSTRUCTIONS})
148
225
  if method == "tools/list":
149
226
  return _respond(msg["id"], {"tools": tool_listing()})
150
227
  if method == "tools/call":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crapkit
3
- Version: 0.4.12
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
  [![PyPI](https://img.shields.io/pypi/v/crapkit)](https://pypi.org/project/crapkit/)
44
44
  [![Python](https://img.shields.io/pypi/pyversions/crapkit)](https://pypi.org/project/crapkit/)
45
45
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/JeanFrancoisGagne/crapkit/blob/main/LICENSE)
46
+ [![crapkit MCP server](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit/badges/score.svg)](https://glama.ai/mcp/servers/JeanFrancoisGagne/crapkit)
46
47
 
47
48
  ![crapkit init, coverage and worklist --top 5 on a small Python repo, then a shell heredoc adding a function at ccn 7: the per-edit advisory reports it and exits 2, and the commit gate refuses the staged file with exit 6](https://raw.githubusercontent.com/JeanFrancoisGagne/crapkit/main/docs/demo.gif)
48
49
 
@@ -165,7 +166,7 @@ leaves the box
165
166
 
166
167
  ```
167
168
  $ crapkit --version
168
- crapkit 0.4.12
169
+ crapkit 0.4.13
169
170
  ```
170
171
 
171
172
  `python -m crapkit` works identically to the console script and is what to use from a
@@ -395,7 +396,7 @@ crapkit ships a `.pre-commit-hooks.yaml` declaring `id: crapkit-gate`. In your
395
396
  repos:
396
397
  - repo: https://github.com/JeanFrancoisGagne/crapkit
397
398
  # crapkit's release step rewrites this line to the tag it just cut
398
- rev: v0.4.12
399
+ rev: v0.4.13
399
400
  hooks:
400
401
  - id: crapkit-gate
401
402
  ```
@@ -501,7 +502,7 @@ to a workflow, and every input has a default:
501
502
  - uses: actions/checkout@v4
502
503
  with:
503
504
  fetch-depth: 0
504
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
505
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
505
506
  ```
506
507
 
507
508
  The whole job those four lines sit in:
@@ -521,7 +522,7 @@ jobs:
521
522
  with:
522
523
  python-version: "3.12" # the interpreter the install below lands in
523
524
  - run: pip install -e ".[dev]" # whatever your lanes need to run
524
- - uses: JeanFrancoisGagne/crapkit@v0.4.12
525
+ - uses: JeanFrancoisGagne/crapkit@v0.4.13
525
526
  with:
526
527
  gate: "false"
527
528
  ```
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes