windbag 0.1.0__py3-none-win_amd64.whl

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.
Binary file
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: windbag
3
+ Version: 0.1.0
4
+ License-File: LICENSE
5
+ Summary: Catches comments that narrate a change instead of documenting a real constraint
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
9
+ Project-URL: Repository, https://github.com/scale-venture-partners/windbag
10
+
11
+ # windbag
12
+
13
+ A pre-commit linter that catches comments narrating a change — a ticket number, what the code used to do, hedging about whether it works — instead of documenting why the current code is the way it is. Checks Python, JavaScript/TypeScript, Terraform/HCL, Rust, and SQL (including dbt and SQLMesh templates), plus the markup formats that carry comments: YAML, HTML, and Markdown.
14
+
15
+ ## What it detects
16
+
17
+ | Rule | Severity | Flags |
18
+ |---|---|---|
19
+ | `TICKET_ID` | error | A ticket ID in a comment (`SCA-533`). Exempts `TODO(SCA-600)`-style tracked tasks and security-advisory IDs (`CVE-`, `GHSA-`, ...). |
20
+ | `HISTORY_NARRATION` | error | "was missing", "used to be", "no longer", "silently swallows", and similar. |
21
+ | `HEDGE_LANGUAGE` | error | "should work", "hopefully", "not sure why", "i believe", and similar. |
22
+ | `CROSS_FILE_REF` | warn | A pointer to another file/line (`handler.py:147`). Documentation URLs are exempt. |
23
+ | `VERBOSE_COMMENT` | warn | A comment that's long relative to what it documents. Markup files are exempt. |
24
+ | `OBVIOUS_COMMENT` | warn | A comment that just restates the line below it (`// increment the counter` above `counter += 1`). |
25
+
26
+ `error` rules fail the check; `warn` rules are reported but don't block.
27
+
28
+ ## Markup files
29
+
30
+ YAML comments (`#`) come from the YAML grammar, so a `#` inside a quoted
31
+ scalar stays data rather than becoming a comment. HTML and Markdown are
32
+ checked through `<!-- ... -->`; in Markdown, anything inside a fenced code
33
+ block is sample markup, not a comment, and is skipped.
34
+
35
+ The content rules — `TICKET_ID`, `HISTORY_NARRATION`, `HEDGE_LANGUAGE`,
36
+ `CROSS_FILE_REF` — carry the weight here. `VERBOSE_COMMENT` does not apply:
37
+ a few lines of explanation above a one-line config key is the idiomatic
38
+ shape in a config file, not a comment outgrowing its code.
39
+
40
+ ## SQL files
41
+
42
+ `.sql` files are read as Jinja-templated SQL, which is what dbt and SQLMesh
43
+ models are. `--`, `/* */`, and Jinja `{# ... #}` comments are all checked. A
44
+ marker inside a `'string'`, a `"quoted identifier"`, a `$$ ... $$` body, or a
45
+ `{{ ... }}` / `{% ... %}` tag is data the template emits, not a comment. The
46
+ scanner is dialect-agnostic, so Snowflake, Postgres, BigQuery, and the rest
47
+ all work; MySQL-style `#` line comments are the one form it does not read.
48
+
49
+ A comment is measured against the statement below it: the non-blank lines
50
+ that follow, through the first one ending in `;`.
51
+
52
+ ## Install
53
+
54
+ Needs a Rust toolchain. If you don't have one:
55
+
56
+ ```bash
57
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
58
+ . "$HOME/.cargo/env" # and add this line to ~/.zshrc
59
+ ```
60
+
61
+ Then, from a checkout:
62
+
63
+ ```bash
64
+ cargo install --path .
65
+ ```
66
+
67
+ That puts `windbag` in `~/.cargo/bin`, which must be on your `PATH`.
68
+
69
+ Without wanting `windbag` on `PATH` permanently: this repo also builds as a
70
+ Python package via [maturin](https://www.maturin.rs)'s `bindings = "bin"`
71
+ mode, which just wraps the compiled binary in a wheel — there's no Python
72
+ code here, and `import windbag` doesn't work. From a checkout with a Rust
73
+ toolchain and [uv](https://docs.astral.sh/uv/):
74
+
75
+ ```bash
76
+ uvx maturin build --release
77
+ uvx --from target/wheels/windbag-*.whl windbag check --all
78
+ ```
79
+
80
+ No package is published yet, so this only works from a local build for now
81
+ — `uvx windbag` (pulling from an index) isn't available.
82
+
83
+ ## Use
84
+
85
+ ```bash
86
+ windbag init # write windbag.toml with generic defaults
87
+ windbag check --staged # check what's about to be committed
88
+ windbag check --all # check every git-tracked file in the repo
89
+ windbag check --json --staged # machine-readable output
90
+ windbag check --new-only f.py # only comments on lines the working tree adds over HEAD
91
+ ```
92
+
93
+ Pre-commit:
94
+
95
+ ```yaml
96
+ - repo: local
97
+ hooks:
98
+ - id: windbag
99
+ name: windbag
100
+ entry: windbag check --staged
101
+ language: system
102
+ pass_filenames: false
103
+ types_or: [python, javascript, jsx, ts, tsx, terraform, rust, yaml, markdown, html, sql]
104
+ ```
105
+
106
+ (`windbag` needs to already be on `PATH` — `language: system` doesn't install it for you.)
107
+
108
+ ## Claude Code plugin
109
+
110
+ Pre-commit catches slop after it's written. The plugin catches it as it's
111
+ written: a `PostToolUse` hook runs `windbag` on every file Claude edits and
112
+ exits non-zero on a violation, so the findings go straight back to Claude as a
113
+ blocking error and it rewrites the comment before moving on. A `SessionStart`
114
+ hook states the rules up front so most edits never trip the linter at all.
115
+
116
+ ```
117
+ /plugin marketplace add scale-venture-partners/windbag
118
+ /plugin install windbag@windbag
119
+ ```
120
+
121
+ The binary has to be on `PATH` too — the plugin ships the hooks, not the linter,
122
+ and they exit quietly when they can't find it. That means a Rust toolchain
123
+ (see [Install](#install)) plus:
124
+
125
+ ```bash
126
+ cargo install --git https://github.com/scale-venture-partners/windbag
127
+ ```
128
+
129
+ TODO: publish prebuilt macOS binaries from a tagged release so installing this
130
+ doesn't require a Rust toolchain. Fine while it's a couple of people; not fine
131
+ as a team-wide ask.
132
+
133
+ To turn it on for everyone working in a given repo, commit this to that repo's
134
+ `.claude/settings.json`. Anyone who opens the repo is prompted to trust the
135
+ marketplace, and the hooks apply from their next session:
136
+
137
+ ```json
138
+ {
139
+ "extraKnownMarketplaces": {
140
+ "windbag": {
141
+ "source": { "source": "github", "repo": "scale-venture-partners/windbag" }
142
+ }
143
+ },
144
+ "enabledPlugins": { "windbag@windbag": true }
145
+ }
146
+ ```
147
+
148
+ Working on the plugin itself? `/plugin marketplace add /path/to/windbag` points
149
+ at a local checkout instead. Either way the install copies [`plugin/`](plugin/)
150
+ into `~/.claude/plugins/cache/` — keeping it out of the repo root is what keeps
151
+ `target/` out of the copy. That copy is a snapshot: after editing a hook,
152
+ reinstall to pick up the change.
153
+
154
+ The hook needs `windbag` on `PATH` (or `WINDBAG_BIN` set) and `jq` installed;
155
+ without either it exits quietly rather than breaking the session.
156
+
157
+ | Env var | Effect |
158
+ |---|---|
159
+ | `WINDBAG_HOOK=off` | Disable both hooks without uninstalling. |
160
+ | `WINDBAG_HOOK_LEVEL=error` | Block only on `error` rules; ignore warnings. |
161
+ | `WINDBAG_BIN` | Explicit path to the binary. |
162
+
163
+ Only comments on lines the working tree adds over `HEAD` are reported, so
164
+ editing a file doesn't re-litigate comments that were already there. Claude is
165
+ told not to silence a rule with `windbag: ignore` on its own — a false positive
166
+ should surface to you, not get suppressed.
167
+
168
+ `/windbag` sweeps the whole repo and fixes what it finds.
169
+
170
+ Suppress a false positive inline:
171
+
172
+ ```python
173
+ # Was missing until v2 (LEGACY-1) — kept for the changelog. windbag: ignore[TICKET_ID]
174
+ ```
175
+
176
+ Config lives in `windbag.toml`; see [`examples/scalevp.toml`](examples/scalevp.toml) for narrowing `TICKET_ID` to a real tracker prefix instead of the generic default.
177
+
178
+ ## Examples
179
+
180
+ ```
181
+ main.tf:218 error TICKET_ID comment references a ticket ID (SCA-533) — that
182
+ context belongs in the commit message or PR
183
+ description, not the code
184
+ main.tf:218 error HISTORY_NARRATION comment narrates the change ("was missing")
185
+ instead of the current state — describe the
186
+ constraint, not the history
187
+ main.tf:218 warn VERBOSE_COMMENT comment block is long relative to what it
188
+ documents (7 comment lines, 7.0x the 1 attached
189
+ code line(s))
190
+ ```
191
+
192
+ ```python
193
+ # Was missing entirely (SCA-533): this used to silently no-op. <- TICKET_ID, HISTORY_NARRATION
194
+ value = fetch_value()
195
+
196
+ # This should work but I'm not sure why it fails sometimes. <- HEDGE_LANGUAGE
197
+ retry(fetch_value)
198
+
199
+ # increment the counter <- OBVIOUS_COMMENT
200
+ counter += 1
201
+
202
+ # TODO(SCA-600): revisit after Q3 pricing model ships <- clean, exempt
203
+ schedule_followup()
204
+
205
+ # Sorted DESC because the caller assumes the first row is newest. <- clean, real WHY
206
+ return sorted(items, reverse=True)
207
+ ```
208
+
209
+ ```yaml
210
+ # Was missing (SCA-533): the deploy no longer fails. <- TICKET_ID, HISTORY_NARRATION
211
+ steps:
212
+ - checkout
213
+
214
+ # Pinned because the orb syntax below requires 2.1. <- clean, real WHY
215
+ version: 2.1
216
+ ```
217
+
218
+ In Markdown, a comment shown as sample markup inside a fence is content:
219
+
220
+ ````markdown
221
+ <!-- Was missing (SCA-533): renders wrong without it. --> <- TICKET_ID, HISTORY_NARRATION
222
+
223
+ ```html
224
+ <!-- Was missing (SCA-901): this one is an example. --> <- clean, inside a fence
225
+ ```
226
+ ````
227
+
228
+ ## License
229
+
230
+ MIT
231
+
@@ -0,0 +1,6 @@
1
+ windbag-0.1.0.data/scripts/windbag.exe,sha256=Td17jIe8HMuY1aMTtBZ1BZOvxdH7ECr7cMmTR9ULFM0,6731776
2
+ windbag-0.1.0.dist-info/METADATA,sha256=9_gZjzMSVK1F_pTQANQqjUVriW9yBvTReDNVnoFDJYU,9156
3
+ windbag-0.1.0.dist-info/WHEEL,sha256=8Aej0W0a6Cz6apA3IzJrTnxLRVLAt-w0Oh8SA3Con_c,94
4
+ windbag-0.1.0.dist-info/licenses/LICENSE,sha256=kKxeedoKI5yoj3tSp8rBpxCQlAPTDD8XWjZY4la8XFk,1085
5
+ windbag-0.1.0.dist-info/sboms/windbag.cyclonedx.json,sha256=VoeY8q52CqJ6qqOrOd7SVpiYX67LpB1_EoJq2rUFies,62696
6
+ windbag-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.15.0)
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ScaleVP
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.