@brokkai/slopcop 0.0.3 → 0.0.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/README.md CHANGED
@@ -1,61 +1,102 @@
1
1
  # SlopCop
2
2
 
3
- SlopCop checks a change through the yet to be deployed hosted SlopCop check
4
- API. The CLI ships the working-tree change to the service, the service applies
5
- it to the base commit and runs the hosted policy catalog plus any repository
6
- rules selected, and the CLI prints the report. There is no local analyzer: a check never needs a
7
- Bifrost install, and the CLI never decides on its own that there is nothing to
8
- check.
9
-
10
- The command is built for agents and one-shot CI: no prompts, one request, and
11
- the analyzer's exit contract.
3
+ **Static analysis deep enough for CI, fast enough to run on every edit.**
4
+
5
+ [![CI](https://github.com/BrokkAi/slopcop/actions/workflows/ci.yml/badge.svg)](https://github.com/BrokkAi/slopcop/actions/workflows/ci.yml)
6
+ [![Release](https://img.shields.io/github/v/release/BrokkAi/slopcop)](https://github.com/BrokkAi/slopcop/releases/latest)
7
+ [![npm](https://img.shields.io/npm/v/@brokkai/slopcop)](https://www.npmjs.com/package/@brokkai/slopcop)
8
+ [![PyPI](https://img.shields.io/pypi/v/brokk-slopcop)](https://pypi.org/project/brokk-slopcop/)
9
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
10
+ [![Go](https://img.shields.io/badge/Go-1.25%2B-blue.svg)](https://go.dev/)
11
+
12
+ `slopcop` sends one git patch, from the base commit to the working tree, to the
13
+ hosted check API at <https://slopcop.com>. The service applies it, runs the
14
+ hosted policy catalog plus the repository rules you selected, and returns the
15
+ report: every finding with its severity, the file and line that triggered it,
16
+ and the certainty behind it. The analysis is
17
+ [Bifrost](https://github.com/BrokkAi/bifrost), an open-source static analysis
18
+ engine rather than a language model, so a finding traces back to a rule instead
19
+ of to a guess.
20
+
21
+ There is no local analyzer and nothing to install per language: the CLI ships
22
+ the change and prints what comes back, and it never decides on its own that
23
+ there is nothing to check. The command is built for agents and one-shot CI: no
24
+ prompts, one request, and the analyzer's exit contract.
12
25
 
13
26
  ## Install
14
27
 
15
- ```console
16
- make install
17
- ```
28
+ npm, PyPI, the release archive, and the PowerShell installer all install the
29
+ same release binary for macOS, Linux, and Windows on x64 and arm64, so
30
+ `slopcop version` reports which release it came from. The Go toolchain can
31
+ build that binary from source instead.
32
+
33
+ | Channel | Platforms | Needs |
34
+ | --- | --- | --- |
35
+ | [npm](#npm) | macOS, Linux, Windows | Node 22 or newer |
36
+ | [PyPI](#pypi) | macOS, Linux, Windows | Python 3.9 or newer |
37
+ | [Release archive](#release-archive) | macOS, Linux, Windows | nothing |
38
+ | [PowerShell installer](#powershell-installer) | Windows | PowerShell |
39
+ | [Go](#go) | built from source | Go 1.25 or newer |
18
40
 
19
- or:
41
+ ### npm
20
42
 
21
43
  ```console
22
- go install github.com/BrokkAi/slopcop/cmd/slopcop@latest
44
+ npm install --global @brokkai/slopcop
45
+ slopcop --help
23
46
  ```
24
47
 
25
- Go 1.25 or newer is required to build.
48
+ The tarball carries the release binary for every supported platform, so it
49
+ needs no Go toolchain. `npx @brokkai/slopcop` runs it without installing it.
26
50
 
27
- On Windows, the PowerShell installer takes the latest release instead of
28
- building:
51
+ ### PyPI
29
52
 
30
- ```powershell
31
- irm https://raw.githubusercontent.com/BrokkAi/slopcop/master/install.ps1 | iex
53
+ ```console
54
+ pip install brokk-slopcop
32
55
  ```
33
56
 
34
- It verifies the release checksum, installs `slopcop.exe` to
35
- `%LOCALAPPDATA%\Programs\slopcop`, and adds that directory to the user `Path`.
36
- `SLOPCOP_VERSION`, `SLOPCOP_INSTALL_DIR`, and `SLOPCOP_NO_MODIFY_PATH` override
37
- its defaults.
57
+ `pipx install brokk-slopcop` or `uv tool install brokk-slopcop` puts it in an
58
+ environment of its own. The wheels carry the release binary, so no Go toolchain
59
+ is needed either. The package is `brokk-slopcop` because PyPI already has an
60
+ unrelated project named `slopcop`; the command it installs is still `slopcop`,
61
+ and `python -m brokk_slopcop` runs the same binary.
38
62
 
39
- The npm package carries the release binary for macOS, Linux, and Windows on x64
40
- and arm64, so it needs no Go toolchain:
63
+ ### Release archive
64
+
65
+ Each release publishes `slopcop_v<version>_<os>_<arch>.tar.gz` (`.zip` on
66
+ Windows) holding the binary, `README.md`, and `LICENSE`, plus a `checksums.txt`
67
+ covering every archive. Download them from
68
+ <https://github.com/BrokkAi/slopcop/releases/latest> and check the sha256
69
+ before unpacking:
41
70
 
42
71
  ```console
43
- npm install --global @brokkai/slopcop
44
- npx @brokkai/slopcop --help
72
+ ver=0.0.3; os=linux; arch=amd64 # darwin/arm64 on Apple silicon
73
+ base=https://github.com/BrokkAi/slopcop/releases/download/v$ver
74
+ curl -fsSLO "$base/slopcop_v${ver}_${os}_${arch}.tar.gz"
75
+ curl -fsSLO "$base/checksums.txt"
76
+ grep "${os}_${arch}" checksums.txt | sha256sum -c - # shasum -a 256 -c - on macOS
77
+ tar -xzf "slopcop_v${ver}_${os}_${arch}.tar.gz" slopcop
78
+ sudo install -m 755 slopcop /usr/local/bin/slopcop
79
+ ```
80
+
81
+ ### PowerShell installer
82
+
83
+ ```powershell
84
+ irm https://raw.githubusercontent.com/BrokkAi/slopcop/master/install.ps1 | iex
45
85
  ```
46
86
 
47
- Node 22 or newer is required to run that package.
87
+ It takes the latest release, verifies the archive against `checksums.txt`,
88
+ installs `slopcop.exe` to `%LOCALAPPDATA%\Programs\slopcop`, and adds that
89
+ directory to the user `Path`. `SLOPCOP_VERSION`, `SLOPCOP_INSTALL_DIR`, and
90
+ `SLOPCOP_NO_MODIFY_PATH` override its defaults.
48
91
 
49
- The PyPI package carries the release binary for those same targets as wheels,
50
- so it needs no Go toolchain either:
92
+ ### Go
51
93
 
52
94
  ```console
53
- pip install brokk-slopcop
95
+ go install github.com/BrokkAi/slopcop/cmd/slopcop@latest
54
96
  ```
55
97
 
56
- The package is `brokk-slopcop` because PyPI already has an unrelated project
57
- named `slopcop`; the command it installs is still `slopcop`, and `python -m
58
- brokk_slopcop` runs the same binary.
98
+ Go 1.25 or newer is required to build. A plain `go install` reports `dev` as
99
+ its version; in a clone, `make install` stamps the version from `git describe`.
59
100
 
60
101
  ## Enable it in an agent
61
102
 
@@ -69,9 +110,12 @@ harness is present, and names what it did:
69
110
  | `~/.claude/skills/slopcop/` | Claude Code, opencode |
70
111
  | `$CODEX_HOME/skills/slopcop/`, or `~/.codex/skills/slopcop/` | Codex home |
71
112
 
72
- A skill that is already there is left alone, so local edits survive and running
73
- init again is safe. `slopcop init --no-skill` skips the install and any update
74
- of it.
113
+ A skill that is already there is left alone by init, so running it again is
114
+ safe. `slopcop login` and every check also install the skill in those
115
+ directories, rewriting one that differs from the skill bundled into the binary,
116
+ so an agent follows the instructions of the slopcop version it runs; local
117
+ edits to an installed skill do not survive them. `slopcop init --no-skill`
118
+ skips the install and any update of it.
75
119
 
76
120
  ## Sign in
77
121
 
@@ -118,10 +162,11 @@ Flags:
118
162
  | `--root PATH` | repository to check (default `.`) |
119
163
  | `--base REV` | base commit or revision for the change |
120
164
  | `--format human\|json\|sarif` | report format (default `human`) |
121
- | `--fail-on never\|finding\|note\|warning\|error` | exit-1 threshold (default `finding`) |
165
+ | `--fail-on never\|finding\|note\|warning\|error` | exit-1 threshold for new findings (default `finding`) |
166
+ | `--show-existing` | also list the findings that were already in the code |
122
167
  | `--policy PATH` | repository-relative `.rqlp` file to evaluate (repeatable) |
123
168
  | `--no-policy-discovery` | skip `.slopcop/policies` rules |
124
- | `--timeout DURATION` | one check request timeout (default `10m`) |
169
+ | `--timeout DURATION` | how long to wait for the result (default `10m`) |
125
170
  | `--verbose` | print the resolved request to stderr |
126
171
 
127
172
  ### What is sent
@@ -141,21 +186,76 @@ Every check also runs the hosted catalog. Rules under
141
186
  `.slopcop/policies/**/*.rqlp` are added automatically unless
142
187
  `--no-policy-discovery` is set.
143
188
 
189
+ ### New and pre-existing findings
190
+
191
+ A check fails only on findings the change introduced or touched: the service
192
+ marks a finding new when it is in a file the change writes and its lines meet a
193
+ line the change added, or the line beside one it removed. Findings that were
194
+ already in the code are counted, never fail the check, and are listed only with
195
+ `--show-existing`. A change that introduces nothing exits `0`, however many
196
+ findings the repository already holds.
197
+
198
+ ### Ignore a false positive
199
+
200
+ Each finding a check reports carries an `id`. When a finding is a false
201
+ positive, ignore it:
202
+
203
+ ```console
204
+ slopcop ignore 0f8fad5b-d9cb-469f-a165-70867728950e
205
+ ```
206
+
207
+ That finding is not reported again for this repository. Ignoring covers that
208
+ one finding, its rule plus its place in the code, in its repository for your
209
+ workspace; the rule keeps reporting everything else. Ignoring a finding twice
210
+ is fine. `ignore` uses the same login and service URL as a check, exits `0`
211
+ with a one-line confirmation, and otherwise exits `2` with the service's
212
+ message and remedy; `finding_not_found` means the id is unknown or comes from a
213
+ check outside your workspace.
214
+
144
215
  ### Output and exit codes
145
216
 
146
217
  The service answers structured data; the CLI renders it. `human` prints one
147
- block per finding, a summary line, and any omitted or incomplete checks. `json`
148
- prints `status`, `baseCommit`, `summary`, `findings`, `incomplete`, and
149
- `failure` when the analysis did not complete. `sarif` prints a SARIF 2.1.0
150
- log.
218
+ block per new finding, with an `ID:` line when the service names one, then the
219
+ pre-existing ones with `--show-existing`, a
220
+ summary line such as `1 new finding across 1 file(s); 72 already in the code
221
+ (--show-existing lists them) | Analysis complete`, and any omitted or
222
+ incomplete checks. `json` prints `status`, `baseCommit`, `summary`
223
+ (`newFindings` and `existingFindings` count the split), `findings` (each with
224
+ `new` and its `id`), `incomplete`, and `failure` when the analysis did not
225
+ complete. `sarif` prints a SARIF 2.1.0 log whose results carry `baselineState`
226
+ `new` or `unchanged` and the finding id in `properties["slopcop.findingId"]`. `--format` and `--show-existing` only change the rendering, never
227
+ the request, so the service answers the same check whichever is used.
151
228
 
152
229
  | Exit | Meaning |
153
230
  | --- | --- |
154
- | `0` | Check completed; nothing crossed the `--fail-on` level |
155
- | `1` | A finding crossed `--fail-on` |
156
- | `2` | The check was incomplete, or the CLI or service failed |
231
+ | `0` | Check completed; no new finding crossed the `--fail-on` level |
232
+ | `1` | A new finding crossed `--fail-on` |
233
+ | `2` | No new finding crossed `--fail-on`, but a check was incomplete; or the CLI or service failed |
234
+ | `3` | The check was still running when `--timeout` ran out |
235
+
236
+ An inconclusive check fails the check only when it concerns a file the
237
+ change writes. One about other files is listed on an `Inconclusive outside
238
+ the change` line (`inconclusiveElsewhere` in `json`, a `note` notification in
239
+ `sarif`) and never changes the exit code. A check that timed out counts
240
+ wherever it is. The service can place an inconclusive check only once Bifrost
241
+ reports which files it concerns; until then every one counts as in the change.
157
242
 
158
243
  The service applies `--fail-on` and sets the status; the exit code follows it.
244
+ The CLI always sends `--fail-on`, defaulting to `finding`: a check fails only
245
+ on findings the change introduced, so any of them, a note or an unrated one
246
+ included, is worth fixing before the change lands. The service's own default
247
+ for a request without `failOn` is Bifrost's `warning`; pass `--fail-on warning`
248
+ to match it.
249
+ New findings that cross `--fail-on` exit `1` even when some check did not
250
+ complete.
251
+
252
+ While the service answers `check_running`, the CLI waits the `Retry-After` it
253
+ names and asks again until `--timeout` runs out; every ask attaches to the same
254
+ running check. If it is still running then, the CLI exits `3`, prints the
255
+ service's message and remedy on stderr, and in `json` prints
256
+ `{"status": "pending", "retryAfter": 5, "message": …, "remedy": …}` on stdout
257
+ (`sarif` prints a run with `slopcop.status` `pending`). Running the same check
258
+ again picks up the same analysis.
159
259
 
160
260
  Service failures print the closed contract exactly: message, remedy, and any
161
261
  install URL. The codes, messages, and remedies are defined by the service in
@@ -164,10 +264,6 @@ rewrite them. The remedies name no client, so when one says to sign in the CLI
164
264
  adds the `slopcop login` command after it. A failed request prints no report
165
265
  on stdout.
166
266
 
167
- While SlopCop is still indexing the repository, the service answers
168
- `indexing_in_progress` at once instead of running the check. The CLI prints it
169
- and exits `2` without waiting or retrying.
170
-
171
267
  ## API
172
268
 
173
269
  The CLI calls `POST <service>/v1/check` with a bearer token:
@@ -184,9 +280,19 @@ The CLI calls `POST <service>/v1/check` with a bearer token:
184
280
 
185
281
  The service rejects any other field. The response carries `status`,
186
282
  `baseCommit`, `applied`, `summary`, `findings`, `incomplete`, and `failure`
187
- when the analysis did not complete. Non-2xx responses carry `code`, `message`,
283
+ when the analysis did not complete. Against a service from before findings
284
+ were marked `new`, the CLI treats every finding as new, as that service gated
285
+ on all of them. Non-2xx responses carry `code`, `message`,
188
286
  `remedy`, `retryable`, and an optional `installUrl`.
189
287
 
288
+ Each finding in the response carries an `id`; a service from before findings
289
+ could be ignored omits it.
290
+
291
+ `slopcop ignore <id>` calls `POST <service>/v1/ignore` with the same bearer
292
+ token and `{"findingId": "<id>"}`, and gets back
293
+ `{"findingId": "<id>", "status": "ignored"}`. Failures use the same closed
294
+ contract, including `finding_not_found`.
295
+
190
296
  `slopcop login` uses the device flow: `POST /api/device` returns a
191
297
  `deviceCode`, `userCode`, `verificationUri`, `expiresIn`, and `interval`, and
192
298
  `POST /api/token` returns `202` while pending or `200` with a `token` once
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,18 @@
1
1
  {
2
2
  "name": "@brokkai/slopcop",
3
- "version": "0.0.3",
4
- "description": "Check a change through the hosted SlopCop check API",
3
+ "version": "0.0.5",
4
+ "description": "Static analysis for coding agents and CI: check a change through the hosted SlopCop API",
5
+ "keywords": [
6
+ "agents",
7
+ "ai-agent",
8
+ "ci",
9
+ "cli",
10
+ "code-review",
11
+ "coding-agent",
12
+ "git",
13
+ "sarif",
14
+ "static-analysis"
15
+ ],
5
16
  "license": "Apache-2.0",
6
17
  "repository": {
7
18
  "type": "git",