stupid-comments 0.1.5
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.
- package/.claude-plugin/marketplace.json +22 -0
- package/LICENSE +674 -0
- package/README.md +408 -0
- package/package.json +70 -0
- package/plugins/stupid-comments/.claude-plugin/plugin.json +9 -0
- package/plugins/stupid-comments/commands/check.md +9 -0
- package/plugins/stupid-comments/commands/fix.md +14 -0
- package/plugins/stupid-comments/commands/off.md +7 -0
- package/plugins/stupid-comments/commands/policy.md +6 -0
- package/plugins/stupid-comments/dsh/cordis.patch.yml +5 -0
- package/plugins/stupid-comments/dsh/index.js +297 -0
- package/plugins/stupid-comments/hooks/hooks.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# stupid-comments
|
|
2
|
+
|
|
3
|
+
**Runtime enforcement for your code comment policy.** A Rust CLI that parses what an LLM is about to write, checks it against *your* policy, and refuses the write when it violates. It ships as a plugin for both [Claude Code](https://claude.com/claude-code) and [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), off the same binary and the same rules.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/nmindz/stupid-comments/releases)
|
|
7
|
+
[](https://www.rust-lang.org)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The problem
|
|
12
|
+
|
|
13
|
+
Every model tier, at every reasoning level, eventually forgets your comment policy and starts writing `// Increment the counter` above `counter++`. That is an attention problem, and no amount of restating the rule in `CLAUDE.md` fixes it — the instruction is simply too far back in the context by the time the code gets written.
|
|
14
|
+
|
|
15
|
+
So this moves enforcement out of the prompt and into the runtime, re-injecting your policy text verbatim at the exact moment it matters.
|
|
16
|
+
|
|
17
|
+
The policy is never baked in. It is yours, it is prose, and it lives where you already keep it.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
git clone https://github.com/nmindz/stupid-comments && cd stupid-comments
|
|
23
|
+
make install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then register the plugin with whichever harness you run. Inside Claude Code:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
/plugin marketplace add nmindz/stupid-comments
|
|
30
|
+
/plugin install stupid-comments@stupid-comments
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or, for DeepSeek Harness:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
dsh plugin --profile tui add github:nmindz/stupid-comments
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Finally, add a `# Comments Policy` section to your agent memory — `~/.claude/CLAUDE.md` or `~/.dsh/AGENTS.md` — in your own words, and confirm it was picked up:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
stupid-comments policy
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Without that section and without a config file, the plugin stays completely silent. There is no default policy, because a default policy would be someone else's taste.
|
|
46
|
+
|
|
47
|
+
## Table of contents
|
|
48
|
+
|
|
49
|
+
- [How it works](#how-it-works)
|
|
50
|
+
- [Installation](#installation)
|
|
51
|
+
- [AI agent instructions](#ai-agent-instructions)
|
|
52
|
+
- [Configuration](#configuration)
|
|
53
|
+
- [Rules](#rules)
|
|
54
|
+
- [CLI usage](#cli-usage)
|
|
55
|
+
- [Slash commands](#slash-commands)
|
|
56
|
+
- [Semantic judging](#semantic-judging)
|
|
57
|
+
- [Escaping it](#escaping-it)
|
|
58
|
+
- [Detecting evasion](#detecting-evasion)
|
|
59
|
+
- [Languages](#languages)
|
|
60
|
+
- [Development](#development)
|
|
61
|
+
- [Known limits](#known-limits)
|
|
62
|
+
- [Contributing](#contributing)
|
|
63
|
+
- [License](#license)
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
Your policy is read from the `# Comments Policy` section of your agent memory (any heading level, case-insensitive). That text is quoted verbatim in every rejection, never paraphrased. If no such section and no config file exist, the plugin does nothing at all and says nothing at all.
|
|
68
|
+
|
|
69
|
+
**Memory is searched in a fixed order,** and the first file carrying the section wins: `$CLAUDE_CONFIG_DIR/CLAUDE.md` (default `~/.claude/CLAUDE.md`), then `$DSH_HOME/AGENTS.md` (default `~/.dsh/AGENTS.md`), then `$AGENTS_HOME/AGENTS.md` (default `~/.agents/AGENTS.md`), then the nearest `CLAUDE.md` and `AGENTS.md` at or above the file being checked. The order is fixed rather than harness-derived on purpose: a machine running both must not get a different policy depending on which agent asked.
|
|
70
|
+
|
|
71
|
+
**Enforcement is layered.** The pre-write gate catches Write/Edit/MultiEdit early, reconstructing the post-edit file in memory so rules see whole-file context while reporting only the lines the edit introduced. The stop gate is the real guarantee: it diffs the working tree and analyzes added lines only, which makes it indifferent to *how* the file was written — heredoc, `sed`, or a subagent all land in the same net.
|
|
72
|
+
|
|
73
|
+
**Both harnesses run the same engine.** Claude Code wires those gates through `PreToolUse`, `Stop`, and `SubagentStop`; DSH wires them through `tools/pre-execute`, `agent/turn-stopping`, and `subagent/end`. Each adapter builds the identical JSON payload and hands it to the same binary, so a rule only ever exists in one place.
|
|
74
|
+
|
|
75
|
+
**Nothing is judged until it is classified.** Every comment is sorted into `directive`, `license-header`, `doc-comment`, or `prose`, and only `prose` faces the ratio and redundancy rules. Lint pragmas, `go:build` lines, shebangs, SPDX headers, and JSDoc are structurally exempt rather than merely tolerated — and a pragma placed above a comment block never launders the block beneath it.
|
|
76
|
+
|
|
77
|
+
**Deletion is not compliance.** A gate you can satisfy by removing the comment trains the model to write none at all, which inverts a policy that asks for *just enough* commenting. So findings name the offending span, demand a rewrite, and say outright that removing it does not count. Bulk deletion is legitimate on a legacy codebase, but only under `/stupid-comments:fix`, where a human is present and there is nothing to game.
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
Two pieces, installed separately and on purpose. The plugin never downloads or executes anything on your behalf — a marketplace plugin that silently fetches a remote binary is exactly the supply-chain pattern worth distrusting.
|
|
82
|
+
|
|
83
|
+
Requires a Rust toolchain. Get one from <https://rustup.rs> if you have none.
|
|
84
|
+
|
|
85
|
+
### 1. The CLI
|
|
86
|
+
|
|
87
|
+
**From a clone (recommended):**
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
make install # installs to ~/.local/bin
|
|
91
|
+
make install ROOT=$HOME/.cargo # or wherever your PATH points
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`make install` runs the cargo command below, then reports what `command -v` actually resolves to and its version.
|
|
95
|
+
|
|
96
|
+
**With cargo directly:**
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
# from a clone
|
|
100
|
+
cargo install --path crates/stupid-comments --root ~/.local --force
|
|
101
|
+
|
|
102
|
+
# or straight from git, without cloning
|
|
103
|
+
cargo install --root ~/.local --git https://github.com/nmindz/stupid-comments stupid-comments
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Drop `--root ~/.local` to use cargo's own default of `~/.cargo/bin`.
|
|
107
|
+
|
|
108
|
+
> [!IMPORTANT]
|
|
109
|
+
> Pick whichever directory is already on your `PATH`. The plugin decides whether to enforce by looking the binary up on `PATH`, so installing somewhere the shell cannot resolve leaves enforcement **permanently inert**. Confirm with `command -v stupid-comments`, not by checking that the file exists.
|
|
110
|
+
|
|
111
|
+
Verify with `stupid-comments --version`.
|
|
112
|
+
|
|
113
|
+
### 2. The plugin
|
|
114
|
+
|
|
115
|
+
**Claude Code:**
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
/plugin marketplace add nmindz/stupid-comments
|
|
119
|
+
/plugin install stupid-comments@stupid-comments
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**DeepSeek Harness:**
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
dsh plugin --profile tui add github:nmindz/stupid-comments
|
|
126
|
+
dsh plugin --profile tui add /path/to/clone # from a checkout
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The package declares a `dsh.bundle` patch, so `dsh plugin add` installs it and reconciles it into that profile's bundle list on its own. Nothing else needs editing.
|
|
130
|
+
|
|
131
|
+
Restart the session so the hooks register. If a policy exists but the CLI is missing, the plugin says so once and enforces nothing.
|
|
132
|
+
|
|
133
|
+
To upgrade later, every piece moves independently:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
make install
|
|
137
|
+
claude plugin marketplace update stupid-comments
|
|
138
|
+
claude plugin update stupid-comments@stupid-comments
|
|
139
|
+
dsh plugin --profile tui update stupid-comments
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 3. A policy
|
|
143
|
+
|
|
144
|
+
Add a `# Comments Policy` section to `~/.claude/CLAUDE.md` or `~/.dsh/AGENTS.md` describing, in your own words, how you want comments written. Confirm it was picked up with `stupid-comments policy`.
|
|
145
|
+
|
|
146
|
+
To keep the policy somewhere else, point at it with the `prose` config key.
|
|
147
|
+
|
|
148
|
+
### Verify
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
stupid-comments check path/to/your/code
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The CLI stands alone, so the same command works as a pre-commit hook or a CI step with `--json`.
|
|
155
|
+
|
|
156
|
+
## AI agent instructions
|
|
157
|
+
|
|
158
|
+
Paste this into a Claude Code session and it will do the setup for you:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
Set up the stupid-comments comment policy enforcer on this machine.
|
|
162
|
+
|
|
163
|
+
1. If `command -v stupid-comments` already resolves, it is installed and
|
|
164
|
+
reachable — skip straight to step 4.
|
|
165
|
+
|
|
166
|
+
2. Pick the install root by checking my PATH FIRST. Never install into a
|
|
167
|
+
directory PATH cannot resolve:
|
|
168
|
+
- if ~/.local/bin is in $PATH -> cargo install --root ~/.local --git \
|
|
169
|
+
https://github.com/nmindz/stupid-comments stupid-comments
|
|
170
|
+
- else if ~/.cargo/bin is in $PATH -> same command without --root
|
|
171
|
+
- else STOP. Do not install. Tell me which directories cargo can target
|
|
172
|
+
and ask which one I want, or give me the export line to add to my
|
|
173
|
+
shell profile first.
|
|
174
|
+
Check with: case ":$PATH:" in *":$HOME/.local/bin:"*) ...
|
|
175
|
+
If cargo itself is missing, point me at https://rustup.rs and stop there.
|
|
176
|
+
|
|
177
|
+
3. Verify by running `command -v stupid-comments` and `stupid-comments
|
|
178
|
+
--version`. If `command -v` does not resolve, the binary went somewhere
|
|
179
|
+
PATH cannot see it — say so plainly instead of reporting success.
|
|
180
|
+
|
|
181
|
+
4. Register the plugin with the harness you are running in.
|
|
182
|
+
In Claude Code, tell me to run these two myself, since you cannot run
|
|
183
|
+
slash commands:
|
|
184
|
+
/plugin marketplace add nmindz/stupid-comments
|
|
185
|
+
/plugin install stupid-comments@stupid-comments
|
|
186
|
+
In DeepSeek Harness, run it yourself and name the profile you targeted:
|
|
187
|
+
dsh plugin --profile <profile> add github:nmindz/stupid-comments
|
|
188
|
+
|
|
189
|
+
5. Read my agent memory — ~/.claude/CLAUDE.md, or ~/.dsh/AGENTS.md under
|
|
190
|
+
DeepSeek Harness — and look for a heading matching "Comments Policy" at
|
|
191
|
+
any level, case-insensitive. If it is missing, DO NOT invent a policy.
|
|
192
|
+
Show me where the section goes, ask what my rules are, and write exactly
|
|
193
|
+
what I tell you.
|
|
194
|
+
|
|
195
|
+
6. Run `stupid-comments policy` and show me the resolved source, mode and rules.
|
|
196
|
+
|
|
197
|
+
7. Explain that mode defaults to `shadow` — findings reported, nothing blocked —
|
|
198
|
+
and that I should stay there until the reports look right before adding a
|
|
199
|
+
.stupid-comments.jsonc with "mode": "block".
|
|
200
|
+
|
|
201
|
+
8. Do not enable the `semantic` option. Tell me it exists, that it spends a
|
|
202
|
+
`claude -p` call per checked file, and that turning it on is my call.
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Configuration
|
|
206
|
+
|
|
207
|
+
Everything here is optional. Drop a `.stupid-comments.jsonc` anywhere at or above the file being checked; the nearest one upward wins.
|
|
208
|
+
|
|
209
|
+
```jsonc
|
|
210
|
+
{
|
|
211
|
+
"mode": "block", // shadow (default) | warn | block
|
|
212
|
+
"bannedPatterns": ["\\bPRDs?[- ]?\\d*\\b"],
|
|
213
|
+
"maxProseCommentLines": 5,
|
|
214
|
+
"maxDocCommentLines": 40,
|
|
215
|
+
"maxCommentRatio": 0.35,
|
|
216
|
+
"minProseCommentsForRatio": 4,
|
|
217
|
+
"redundancy": "warn",
|
|
218
|
+
"semantic": "shadow", // shadow (default) | warn | block
|
|
219
|
+
"exclude": ["**/generated/**"]
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Key | Type | Default | Purpose |
|
|
224
|
+
| --- | --- | --- | --- |
|
|
225
|
+
| `mode` | `shadow` \| `warn` \| `block` | `shadow` | Global severity ceiling. `shadow` reports without blocking |
|
|
226
|
+
| `prose` | path | — | Read the policy from this file instead of agent memory. `~` expands |
|
|
227
|
+
| `maxProseCommentLines` | integer | `5` | Longest permitted prose comment block |
|
|
228
|
+
| `maxDocCommentLines` | integer | `40` | Longest permitted doc comment |
|
|
229
|
+
| `maxCommentRatio` | float | `0.35` | Share of a file that may be prose comments |
|
|
230
|
+
| `minProseCommentsForRatio` | integer | `4` | Comment *blocks* required before the ratio rule applies |
|
|
231
|
+
| `bannedPatterns` | regex list | empty | Text that may never appear in a comment |
|
|
232
|
+
| `redundancy` | `shadow` \| `warn` \| `block` | `warn` | Comments that restate the line below them |
|
|
233
|
+
| `semantic` | `shadow` \| `warn` \| `block` | `shadow` | LLM taste judgement. See [Semantic judging](#semantic-judging) |
|
|
234
|
+
| `semanticCommand` | string list | `["claude", "-p"]` | Command the semantic judge shells out to |
|
|
235
|
+
| `exclude` | glob list | empty | Paths to skip entirely |
|
|
236
|
+
|
|
237
|
+
These are *calibration*, not policy. The defaults are deliberately loose, because a threshold tight enough to be opinionated would be smuggling in someone else's taste.
|
|
238
|
+
|
|
239
|
+
> [!TIP]
|
|
240
|
+
> `mode` defaults to `shadow`: findings are reported, nothing is blocked. Stay there until the log convinces you the blocks would have been right, then switch to `block`.
|
|
241
|
+
|
|
242
|
+
An unparseable config is an error, not silence — `check` and `policy` print the reason and exit non-zero. Only the hook still fails open, since an unreadable config must never block a write.
|
|
243
|
+
|
|
244
|
+
## Rules
|
|
245
|
+
|
|
246
|
+
| Rule | Default severity | Fires when |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| `banned-pattern` | block | A comment matches one of your `bannedPatterns` |
|
|
249
|
+
| `prose-comment-too-long` | block | A prose block exceeds `maxProseCommentLines` |
|
|
250
|
+
| `doc-comment-too-long` | warn | A doc comment exceeds `maxDocCommentLines` |
|
|
251
|
+
| `comment-ratio` | block | Prose comments cover more than `maxCommentRatio` of the file |
|
|
252
|
+
| `redundant-comment` | warn | A comment restates the code directly below it |
|
|
253
|
+
| `semantic` | warn | The judge decides a comment has not earned its place |
|
|
254
|
+
| `comments-removed` | warn | A file that had prose comments now has none |
|
|
255
|
+
|
|
256
|
+
Severities are ceilings, not floors: under `"mode": "shadow"` or `"warn"` every finding is downgraded, and findings recovered from a file whose grammar failed are always warn-only.
|
|
257
|
+
|
|
258
|
+
## CLI usage
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
stupid-comments check [PATH]... # report findings, change nothing
|
|
262
|
+
stupid-comments check --json # machine-readable, for CI
|
|
263
|
+
stupid-comments check --adjudicate # permit deletion as a remedy
|
|
264
|
+
stupid-comments policy # show the resolved policy and its source
|
|
265
|
+
stupid-comments hook claude|dsh # consume a hook payload on stdin
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Every run prints a coverage summary to **stderr**, leaving stdout clean for `--json`:
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
Checked 17 files (rust 12, json 2, toml 2, make 1).
|
|
272
|
+
Not checked — no grammar for 8 files: .md 5, .gitignore 1, .lock 1, LICENSE 1
|
|
273
|
+
Not checked — excluded by config: 10 files
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
A file with no grammar is not a passing file, so it is never folded into the checked count. The numbers add up on purpose.
|
|
277
|
+
|
|
278
|
+
| Exit code | Meaning |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| `0` | No blocking findings |
|
|
281
|
+
| `1` | A blocking finding, an unreadable config, or a path that does not exist |
|
|
282
|
+
| `2` | Hook only: block the pending write |
|
|
283
|
+
|
|
284
|
+
## Slash commands
|
|
285
|
+
|
|
286
|
+
| Claude Code | DeepSeek Harness | Purpose |
|
|
287
|
+
| --- | --- | --- |
|
|
288
|
+
| `/stupid-comments:policy` | `/stupid-comments-policy` | Show the policy in force and where it came from |
|
|
289
|
+
| `/stupid-comments:check [path]` | `/stupid-comments-check [path]` | Report findings, change nothing |
|
|
290
|
+
| `/stupid-comments:fix [path]` | `/stupid-comments-fix [path]` | Adjudicated sweep of an existing codebase; deletion permitted |
|
|
291
|
+
| `/stupid-comments:off` | `/stupid-comments-off` | How to disarm for a session |
|
|
292
|
+
|
|
293
|
+
The names differ only because DSH command names cannot carry a colon. The prompts do not: both harnesses read the same markdown files under `plugins/stupid-comments/commands/`, so the wording has exactly one home.
|
|
294
|
+
|
|
295
|
+
## Semantic judging
|
|
296
|
+
|
|
297
|
+
Deterministic rules cannot decide whether a comment earns its place. Setting `"semantic": "warn"` (or `"block"`) sends the prose comments and your policy text to `claude -p`, using the session authentication you already have — there is no API key to configure and none is wanted. Every failure is silent: no `claude` on PATH, a timeout, unparseable output, all mean no findings.
|
|
298
|
+
|
|
299
|
+
The judge is a subprocess, not a harness binding. Point `semanticCommand` at anything that reads a prompt on stdin and answers with JSON, and it works the same from either plugin.
|
|
300
|
+
|
|
301
|
+
It is off by default because it spends a model call per checked file. It is also the only rule that catches `// Adds a and b` sitting above `const sum = a + b`, which is probably the comment that made you look for this tool.
|
|
302
|
+
|
|
303
|
+
## Escaping it
|
|
304
|
+
|
|
305
|
+
Set `STUPID_COMMENTS=0` in the session environment. That is deliberately the only mid-session hatch — it lives somewhere the model cannot write, so the enforced party cannot disable its own gate. Both plugins honor it, and the DSH one registers no seams at all when it is set.
|
|
306
|
+
|
|
307
|
+
Permanently: change `mode` in `.stupid-comments.jsonc`, or remove the plugin with `/plugin uninstall stupid-comments@stupid-comments` or `dsh plugin --profile tui remove stupid-comments`.
|
|
308
|
+
|
|
309
|
+
Suppression pragmas exist, but they are anchored to git:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
// stupid-comments: ignore -> suppresses findings on the next 3 lines
|
|
313
|
+
// stupid-comments: ignore-file -> suppresses the whole file
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
> [!NOTE]
|
|
317
|
+
> A pragma is honored **only if the identical line already exists in `HEAD`**. One introduced in the same change as the violation it silences is ignored entirely, so the model cannot write its own exemption. Outside a git repository no pragma is honored.
|
|
318
|
+
|
|
319
|
+
## Detecting evasion
|
|
320
|
+
|
|
321
|
+
A gate that counts only violations cannot tell "learned taste" from "stopped writing comments". Prose-comment counts are tracked per file for the session, and a file that had comments and now has none raises a `comments-removed` warning naming what was lost. Bulk removal is legitimate, but only under `/stupid-comments:fix`, where a human asked for it.
|
|
322
|
+
|
|
323
|
+
## Languages
|
|
324
|
+
|
|
325
|
+
JavaScript, TypeScript, TSX/JSX, Rust, Go, Kotlin, JSON/JSONC/JSON5, TOML, YAML, HCL/Terraform, shell (sh/bash/zsh/ksh), and Make, via native tree-sitter grammars.
|
|
326
|
+
|
|
327
|
+
**Not every file carries its language in its extension.** `Makefile`, `GNUmakefile`, `Makefile.*` and `*.mk` are matched by name, as are the usual shell rc files, and an extensionless file is checked for a shell shebang — a `scripts/` directory is mostly extensionless, and skipping one silently is indistinguishable from checking it and finding nothing. `#!/usr/bin/env bash` counts; `#!/usr/bin/env python3` does not, and neither does `fish`.
|
|
328
|
+
|
|
329
|
+
**A `#` inside a shell string, a heredoc body, or a Make recipe is data, not commentary.** Telling those apart is the whole reason this uses grammars rather than a regex over lines starting with `#`.
|
|
330
|
+
|
|
331
|
+
**Config formats answer to exactly the same rules as code**, `maxCommentRatio` included. They have twice been given something gentler — first an outright exemption from the ratio rule, then a looser threshold of their own — and both times the result was a manifest sitting at a comment load that would be flagged on sight in a `.go` file. A YAML at 43% comments is a YAML at 43% comments; there is no version of "just enough" that reads differently because the file ends in `.yaml`.
|
|
332
|
+
|
|
333
|
+
**Templating defeats the YAML grammar.** A Helm chart parses to a single error node with no comments in it, which would make every templated manifest in a repository look clean. When the grammar fails on a `#`-comment format, comments are recovered by a line scan instead — whole-line comments only, block scalars left alone, so the failure direction is a missed comment rather than an invented one.
|
|
334
|
+
|
|
335
|
+
Failure is otherwise open. Parse error, missing binary, unreadable config — inside the hook all of them mean *no findings*, never a blocked write.
|
|
336
|
+
|
|
337
|
+
## Development
|
|
338
|
+
|
|
339
|
+
Requires a Rust toolchain. `make help` lists every target.
|
|
340
|
+
|
|
341
|
+
| Make | Cargo equivalent | Purpose |
|
|
342
|
+
| --- | --- | --- |
|
|
343
|
+
| `make build` | `cargo build --release` | Compile the release binary |
|
|
344
|
+
| `make test` | `cargo test` | Run the test suite |
|
|
345
|
+
| `make dsh-test` | `node plugins/stupid-comments/dsh/test.mjs` | Drive the DSH adapter against the release binary |
|
|
346
|
+
| `make lint` | `cargo clippy --all-targets` | Lint every target |
|
|
347
|
+
| `make version` | `node scripts/sync-version.mjs X.Y.Z` | Write one version into all five manifests |
|
|
348
|
+
| `make validate` | `claude plugin validate` + `scripts/validate-dsh-manifest.mjs` | Check both plugin manifests |
|
|
349
|
+
| `make check` | all of the above | Everything CI would run |
|
|
350
|
+
| `make install` | `cargo install --path crates/stupid-comments --root ~/.local --force` | Install the binary |
|
|
351
|
+
| `make uninstall` | `cargo uninstall --root ~/.local stupid-comments` | Remove it |
|
|
352
|
+
| `make dsh-install` | `dsh plugin --profile tui add $(pwd)` | Register this checkout with a dsh profile |
|
|
353
|
+
| `make dsh-uninstall` | `dsh plugin --profile tui remove stupid-comments` | Unregister it |
|
|
354
|
+
| `make selfcheck` | `./target/release/stupid-comments check .` | Enforce this repo's policy on itself |
|
|
355
|
+
| `make clean` | `cargo clean` | Remove build artifacts |
|
|
356
|
+
|
|
357
|
+
`make selfcheck` is the one that matters: the enforcer answers to its own policy, and a change that makes this repo fail its own gate is not ready.
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
crates/stupid-comments/src/
|
|
361
|
+
├── lang.rs # language detection and grammar bindings
|
|
362
|
+
├── comments.rs # extraction and classification
|
|
363
|
+
├── rules.rs # the deterministic rules
|
|
364
|
+
├── semantic.rs # the opt-in LLM judge
|
|
365
|
+
├── policy.rs # config and policy resolution
|
|
366
|
+
├── hook.rs # hook payloads, shared by every harness
|
|
367
|
+
├── suppress.rs # git-anchored pragmas
|
|
368
|
+
├── session.rs # cross-turn evasion tracking
|
|
369
|
+
└── main.rs # CLI
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The harness plugins are adapters over that binary, and neither carries a rule of its own:
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
plugins/stupid-comments/
|
|
376
|
+
├── .claude-plugin/plugin.json # Claude Code manifest
|
|
377
|
+
├── hooks/hooks.json # Claude Code hook wiring
|
|
378
|
+
├── commands/*.md # slash command prompts, read by both harnesses
|
|
379
|
+
└── dsh/
|
|
380
|
+
├── index.js # DSH cordis plugin: seams, payloads, commands
|
|
381
|
+
├── cordis.patch.yml # the bundle layer dsh composes
|
|
382
|
+
└── test.mjs # drives the adapter against the real binary
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`package.json` at the repo root is the DSH bundle manifest: it points `dsh.bundle.patch` at that patch file, which is the whole reason `dsh plugin add` can install this repository directly.
|
|
386
|
+
|
|
387
|
+
Releases are derived from Conventional Commits by semantic-release, and the npm package is *staged* rather than published: CI authenticates through OIDC trusted publishing and holds no credential that can ship a version on its own, so a human approves the tarball with a 2FA code. See [CONTRIBUTING.md](CONTRIBUTING.md) for the commit convention, the release flow, and a walkthrough of adding a language.
|
|
388
|
+
|
|
389
|
+
## Known limits
|
|
390
|
+
|
|
391
|
+
- Redundancy detection is warn-only. It is the most false-positive-prone rule here and has not earned blocking authority.
|
|
392
|
+
- Kotlin findings are warn-only while its grammar earns trust, as are findings recovered by line scan from a templated config file.
|
|
393
|
+
- The line-scan fallback reads whole-line comments only, so a trailing `# comment` on a value line goes unchecked in a templated file.
|
|
394
|
+
- Python has no grammar yet, so `.py` files are named as unchecked rather than checked.
|
|
395
|
+
- `minProseCommentsForRatio` counts comment *blocks*, not lines, so a file carrying fewer than four separate blocks never trips the ratio rule however much of the file they cover. Long blocks are caught by the length rule instead.
|
|
396
|
+
- Semantic judging costs a model call per checked file, so it is off by default.
|
|
397
|
+
- The `Stop` gate diffs against `HEAD`, so a tree that was already dirty before the session has those earlier changes considered too.
|
|
398
|
+
- DSH also ships a text-editor tool. Its `create` and `str_replace` commands are translated and checked before the write; its `insert` command carries no anchor to reconstruct from, so it falls to the stop gate.
|
|
399
|
+
- Under DSH, `subagent/end` is an observation point rather than a decision point. A subagent that ends on a violation is handed the finding as context; only the parent's own stop gate can force the rewrite.
|
|
400
|
+
- The DSH plugin reports a missing binary the first time a write is about to be checked, not at session start, so a session that never writes code stays silent about it.
|
|
401
|
+
|
|
402
|
+
## Contributing
|
|
403
|
+
|
|
404
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
GPL-3.0-or-later. See [LICENSE](LICENSE).
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stupid-comments",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Enforces your comment policy at write time, inside DeepSeek Harness. Reads the policy from your AGENTS.md or CLAUDE.md, blocks violating writes, and re-injects the policy verbatim so the model stops drifting from it.",
|
|
5
|
+
"license": "GPL-3.0-or-later",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "nmindz",
|
|
8
|
+
"url": "https://github.com/nmindz"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "plugins/stupid-comments/dsh/index.js",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./plugins/stupid-comments/dsh/index.js",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"plugins/stupid-comments/**",
|
|
18
|
+
"!plugins/stupid-comments/dsh/test.mjs",
|
|
19
|
+
".claude-plugin/**",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22.13.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"registry": "https://registry.npmjs.org/"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@commitlint/cli": "^21.2.2",
|
|
32
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
33
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
34
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
35
|
+
"@semantic-release/exec": "^7.1.0",
|
|
36
|
+
"@semantic-release/git": "^11.0.1",
|
|
37
|
+
"@semantic-release/github": "^12.0.9",
|
|
38
|
+
"@semantic-release/npm": "^13.1.5",
|
|
39
|
+
"semantic-release": "^25.0.9"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"dsh",
|
|
43
|
+
"dsh-plugin",
|
|
44
|
+
"deepseek-harness",
|
|
45
|
+
"claude-code",
|
|
46
|
+
"comments",
|
|
47
|
+
"policy",
|
|
48
|
+
"linting",
|
|
49
|
+
"code-quality",
|
|
50
|
+
"hooks"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/nmindz/stupid-comments.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/nmindz/stupid-comments#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/nmindz/stupid-comments/issues"
|
|
59
|
+
},
|
|
60
|
+
"dsh": {
|
|
61
|
+
"bundle": {
|
|
62
|
+
"patch": "./plugins/stupid-comments/dsh/cordis.patch.yml"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"test": "node plugins/stupid-comments/dsh/test.mjs",
|
|
67
|
+
"validate": "node --check plugins/stupid-comments/dsh/index.js && node scripts/validate-dsh-manifest.mjs",
|
|
68
|
+
"version:sync": "node scripts/sync-version.mjs"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stupid-comments",
|
|
3
|
+
"description": "Enforces your comment policy at write time. Reads the policy from your CLAUDE.md, blocks violating writes, and re-injects the policy verbatim so the model stops drifting from it.",
|
|
4
|
+
"version": "0.1.5",
|
|
5
|
+
"author": { "name": "nmindz", "url": "https://github.com/nmindz" },
|
|
6
|
+
"license": "GPL-3.0-or-later",
|
|
7
|
+
"repository": "https://github.com/nmindz/stupid-comments",
|
|
8
|
+
"keywords": ["comments", "policy", "linting", "code-quality", "hooks"]
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check files or directories against the comment policy without changing anything.
|
|
3
|
+
argument-hint: [path]
|
|
4
|
+
allowed-tools: Bash(stupid-comments check:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Run `stupid-comments check ${1:-.}` and summarize the findings by rule. Do not edit any files — this command reports only.
|
|
8
|
+
|
|
9
|
+
Report the coverage line the tool prints alongside the findings. A file it had no grammar for was not checked, and reporting it as clean is how an entire directory goes unexamined. If the extensions listed as unchecked look like they carry comments, say so plainly rather than calling the sweep complete.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Sweep an existing codebase into compliance. You act as adjudicator and may delete comments.
|
|
3
|
+
argument-hint: [path]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are running the comment policy sweep. Unlike the turn-by-turn gate, deletion **is** a valid remedy here, because a human invoked this command and there is no incentive to game it.
|
|
7
|
+
|
|
8
|
+
Procedure, in order:
|
|
9
|
+
|
|
10
|
+
1. Refuse to continue if the working tree is dirty. Tell the user to commit or stash first — this rewrites files in bulk and git is the only escape hatch.
|
|
11
|
+
2. Run `stupid-comments check ${1:-.} --adjudicate --json` and group the findings by file.
|
|
12
|
+
3. Present a summary and stop. This is a dry run; do not edit yet. State the coverage line as part of that summary: name the extensions the tool could not parse and say those files are unexamined. Do not describe the sweep as covering the repository when it covered the parseable part of it.
|
|
13
|
+
4. Only after the user approves, rewrite the offending comments. Condense what carries meaning, delete what is pure noise, and never touch anything classified as `directive` or `license-header`.
|
|
14
|
+
5. Commit the result as a single revision so the user can revert it wholesale.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Explain how to disarm enforcement for a session.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Tell the user that enforcement is disarmed by setting `STUPID_COMMENTS=0` in the environment of the Claude Code session, and that this is deliberately the only mid-session escape hatch: it lives outside anything you can write to a file, so you cannot disable the gate on your own behalf.
|
|
6
|
+
|
|
7
|
+
For a permanent change, point them at `mode` in `.stupid-comments.jsonc` (`shadow`, `warn`, or `block`) or `/plugin uninstall stupid-comments@stupid-comments`.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show the comment policy currently in force and where it came from.
|
|
3
|
+
allowed-tools: Bash(stupid-comments policy)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run `stupid-comments policy` and report the resolved source, mode, and rule values back to the user. If it reports no policy, explain that enforcement is inert until a `# Comments Policy` section exists in `~/.claude/CLAUDE.md` or a `.stupid-comments.jsonc` is present.
|