@brokkai/slopcop 0.0.7 → 0.0.9

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
@@ -25,16 +25,17 @@ prompts, one request, and the analyzer's exit contract.
25
25
 
26
26
  ## Install
27
27
 
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.
28
+ npm, PyPI, the release archive, and the shell and PowerShell installers all
29
+ install the same release binary for macOS, Linux, and Windows on x64 and
30
+ arm64, so `slopcop version` reports which release it came from. The Go
31
+ toolchain can build that binary from source instead.
32
32
 
33
33
  | Channel | Platforms | Needs |
34
34
  | --- | --- | --- |
35
35
  | [npm](#npm) | macOS, Linux, Windows | Node 22 or newer |
36
36
  | [PyPI](#pypi) | macOS, Linux, Windows | Python 3.9 or newer |
37
37
  | [Release archive](#release-archive) | macOS, Linux, Windows | nothing |
38
+ | [Shell installer](#shell-installer) | macOS, Linux | curl |
38
39
  | [PowerShell installer](#powershell-installer) | Windows | PowerShell |
39
40
  | [Go](#go) | built from source | Go 1.25 or newer |
40
41
 
@@ -78,6 +79,17 @@ tar -xzf "slopcop_v${ver}_${os}_${arch}.tar.gz" slopcop
78
79
  sudo install -m 755 slopcop /usr/local/bin/slopcop
79
80
  ```
80
81
 
82
+ ### Shell installer
83
+
84
+ ```console
85
+ curl -fsSL https://raw.githubusercontent.com/BrokkAi/slopcop/master/install.sh | sh
86
+ ```
87
+
88
+ It takes the latest release, verifies the archive against `checksums.txt`,
89
+ installs `slopcop` to `$HOME/.local/bin`, and adds that directory to the
90
+ shell profile. `SLOPCOP_VERSION`, `SLOPCOP_INSTALL_DIR`, and
91
+ `SLOPCOP_NO_MODIFY_PATH` override its defaults.
92
+
81
93
  ### PowerShell installer
82
94
 
83
95
  ```powershell
@@ -89,6 +101,25 @@ installs `slopcop.exe` to `%LOCALAPPDATA%\Programs\slopcop`, and adds that
89
101
  directory to the user `Path`. `SLOPCOP_VERSION`, `SLOPCOP_INSTALL_DIR`, and
90
102
  `SLOPCOP_NO_MODIFY_PATH` override its defaults.
91
103
 
104
+ ### Upgrade notice
105
+
106
+ Every response from the service names its newest release in the
107
+ `SlopCop-Latest-Version` header. When that is newer than the running build,
108
+ the CLI prints one line on stderr, never stdout, so `--format json` and
109
+ `sarif` stay clean:
110
+
111
+ ```text
112
+ slopcop v0.0.9 is available (you have v0.0.7). Upgrade: npm install --global @brokkai/slopcop@latest
113
+ ```
114
+
115
+ The command follows the channel the binary was installed through (npm, npx,
116
+ pip, pipx, or uv), judged by where it lives, and links back here otherwise.
117
+ The notice prints at most once a day; the time of the last one is kept in
118
+ `slopcop/update-notice` next to the config. `dev` builds, `Stop` hook runs, and
119
+ runs with `$SLOPCOP_NO_UPDATE_NOTICE` set to any non-empty value never print
120
+ it. A service that no longer accepts a version answers `client_outdated`
121
+ (HTTP 426), which exits `2`.
122
+
92
123
  ### Go
93
124
 
94
125
  ```console
@@ -101,8 +132,9 @@ its version; in a clone, `make install` stamps the version from `git describe`.
101
132
  ## Enable it in an agent
102
133
 
103
134
  The binary carries the agent skill that teaches a coding agent how to run
104
- slopcop. `slopcop init` installs it into each well-known skills directory whose
105
- harness is present, and names what it did:
135
+ slopcop, plus an automatic check hook for Claude Code and Codex. `slopcop init`
136
+ installs the skill into each well-known skills directory whose harness is
137
+ present, and names what it did:
106
138
 
107
139
  | Location | Harness |
108
140
  | --- | --- |
@@ -117,6 +149,47 @@ so an agent follows the instructions of the slopcop version it runs; local
117
149
  edits to an installed skill do not survive them. `slopcop init --no-skill`
118
150
  skips the install and any update of it.
119
151
 
152
+ Init also adds a `Stop` hook for each harness installed here, and leaves
153
+ everything else in the file as it was:
154
+
155
+ | File | Harness |
156
+ | --- | --- |
157
+ | `settings.json` under `CLAUDE_CONFIG_DIR`, or `~/.claude` | Claude Code |
158
+ | `hooks.json` under `CODEX_HOME`, or `~/.codex` | Codex |
159
+
160
+ Codex runs a new or changed hook only once you trust it: open Codex and review
161
+ it with `/hooks`. Init never writes that trust itself, and reminds you each
162
+ time it runs, since Codex says nothing of a hook it skips. The hook stays the
163
+ same from one release to the next, so an update does not ask for it again.
164
+
165
+ When the agent stops, the hook checks the working tree through the same
166
+ hosted API, but only when all of these hold:
167
+
168
+ - the session wrote files in the repository with its edit tools (a Codex
169
+ session's subagents included);
170
+ - slopcop is signed in, and the workspace a check would use
171
+ (`$SLOPCOP_WORKSPACE`, or the login's) has the repository active
172
+ (`slopcop repo list`), so nothing is sent from other repositories;
173
+ - the tree differs from the last check in the session, including a check the
174
+ agent ran by hand.
175
+
176
+ New findings in files the session wrote are handed to the agent to fix, once
177
+ for a given tree and at most twice in a row; then the agent is let go and you
178
+ are told what remains. A finding ignored with `slopcop ignore` is not handed
179
+ back. New findings in other files on the branch are only mentioned. The hook
180
+ never keeps the agent going on its own failures: a refusal the service calls
181
+ final, such as an expired login, is reported once and the hook stays quiet for
182
+ the rest of the session, until a check by hand completes; a brief outage is
183
+ only reported. The hook runs as `slopcop hook claude || true` (and
184
+ `slopcop hook codex || true`, or `|| exit /b 0` under Windows' `cmd`), so even
185
+ a `slopcop` that cannot run it, such as an older one first on `PATH`, lets the
186
+ agent stop; init warns when that is the case, because no check would run.
187
+
188
+ Use `slopcop init --no-hooks` to install only the skill, or
189
+ `slopcop init --remove-hooks` to remove SlopCop's hooks and nothing else.
190
+ Ordinary checks do not reinstall a removed hook. The skill remains the way to
191
+ check from other harnesses.
192
+
120
193
  ## Sign in
121
194
 
122
195
  ```console
@@ -137,6 +210,7 @@ Precedence:
137
210
  | --- | --- |
138
211
  | Service URL | `--service`, `$SLOPCOP_SERVICE`, stored by `slopcop login`, `https://slopcop.com` |
139
212
  | Token | `$SLOPCOP_TOKEN`, stored value |
213
+ | Workspace (`check`, `ignore`, `repo`) | `-w`/`--workspace`, `$SLOPCOP_WORKSPACE`, the one the login is bound to |
140
214
 
141
215
  ## Workspace and repositories
142
216
 
@@ -147,6 +221,7 @@ active in the browser when the login was approved.
147
221
  slopcop whoami # account, this login's workspace, its plan, the service URL
148
222
  slopcop workspace list # the account's workspaces; * marks this login's
149
223
  slopcop workspace use "Acme team" # move this login (not the browser) to another, by name or id
224
+ slopcop check -w "Acme team" # check in, and bill, another workspace this once
150
225
  slopcop repo list # the workspace's repositories: active, disabled, or over the limit
151
226
  slopcop repo disable acme/widgets # stop checking and charging for one (owner only)
152
227
  slopcop repo enable acme/widgets # turn it back on, within the workspace's active limit
@@ -160,9 +235,23 @@ one is not checked or charged and keeps its reports, findings and settings.
160
235
  and refuse when stdin is not a terminal: an agent or CI job runs with your
161
236
  login, and the service cannot tell it from you, so changing what the workspace
162
237
  pays stays a step you take yourself.
238
+ `workspace use` moves every agent and script using this login, not only the
239
+ one that runs it, so it asks `[y/N]` and refuses when stdin is not a terminal.
240
+ To check in another workspace without moving anyone, `check`, `ignore` and
241
+ `repo` take `-w`/`--workspace <name|id>` (or `$SLOPCOP_WORKSPACE`) for that one
242
+ command; the login stays where it was. Give agents that share a login their
243
+ own `--workspace` instead of switching it.
244
+
163
245
  Names match without regard to case; when two workspaces share a name, or two
164
246
  providers a repository name, pass the id the listing prints. There is no `repo
165
- add`: a first check adds a repository. Deleting repositories, archiving
247
+ add`: a first check adds a repository. When the workspace would then pay for
248
+ it (it already has an active repository and is charged), the service refuses
249
+ the check with `repository_add_unconfirmed` until a person confirms: at a
250
+ terminal `check` prints what it costs, asks `Add owner/name to "Workspace"?
251
+ [y/N]`, and on a yes sends the check again; with no terminal, as for an agent
252
+ or CI, it exits `4` with the service's remedy and, in `json`, a `failed`
253
+ report naming the code. A workspace's first repository
254
+ is added without asking. Deleting repositories, archiving
166
255
  workspaces, members, credits and repository settings stay in the web app. The
167
256
  commands exit `0` on success and `2` with the service's message and remedy
168
257
  otherwise, as `ignore` does.
@@ -176,13 +265,19 @@ slopcop --root ../repo --base HEAD~1
176
265
  ```
177
266
 
178
267
  The base commit defaults to the merge base with the branch upstream, then the
179
- merge base with `origin/HEAD`, then `HEAD`. The service must already have the
268
+ merge base with `origin/HEAD`, then `HEAD` when neither is known. When the
269
+ upstream merge base yields an empty patch, as on a branch whose commits are all
270
+ pushed, the CLI uses the merge base with `origin/HEAD` instead, so a check
271
+ right after a push still covers the branch. The service must already have the
180
272
  base commit in its clone; a missing one comes back as the service's
181
273
  `base_not_found` message and remedy.
182
274
 
183
275
  Every run sends one check request, even when the working tree matches the base
184
276
  and the patch is empty. A clean result or a failure always comes from the
185
- service.
277
+ service. A report for an empty patch comes with a note on stderr that suggests
278
+ `--base`, and `json` adds `"emptyChange": true` (`sarif`:
279
+ `properties["slopcop.emptyChange"]`): a clean result then means nothing
280
+ changed was checked.
186
281
 
187
282
  Flags:
188
283
 
@@ -195,6 +290,7 @@ Flags:
195
290
  | `--policy PATH` | repository-relative `.rqlp` file to evaluate (repeatable) |
196
291
  | `--no-policy-discovery` | skip `.slopcop/policies` rules |
197
292
  | `--timeout DURATION` | how long to wait for the result (default `10m`) |
293
+ | `-w`, `--workspace NAME\|ID` | check in, and bill, this workspace of the account instead of the login's |
198
294
  | `--verbose` | print the resolved request to stderr |
199
295
 
200
296
  ### What is sent
@@ -272,8 +368,8 @@ summary line such as `1 new finding across 1 file(s); 72 already in the code |
272
368
  Analysis complete`, and any omitted new findings or
273
369
  incomplete checks. `json` prints `status`, `baseCommit`, `summary`
274
370
  (`newFindings` and `existingFindings` count the split), `findings` (each with
275
- `new` and its `id`), `incomplete`, and `failure` when the analysis did not
276
- complete. `sarif` prints a SARIF 2.1.0 log whose results carry `baselineState`
371
+ `new` and its `id`), `incomplete`, `failure` when the analysis did not
372
+ complete, and `emptyChange` when the patch was empty. `sarif` prints a SARIF 2.1.0 log whose results carry `baselineState`
277
373
  `new` and the finding id in `properties["slopcop.findingId"]`. `--format` only
278
374
  changes the rendering, never the request, so the service answers the same check
279
375
  whichever is used.
@@ -284,6 +380,7 @@ whichever is used.
284
380
  | `1` | A new finding crossed `--fail-on` |
285
381
  | `2` | No new finding crossed `--fail-on`, but a check was incomplete; or the CLI or service failed |
286
382
  | `3` | The check was still running when `--timeout` ran out |
383
+ | `4` | The service refused the check, and only a person can clear it |
287
384
  | `130`, `143` | Interrupted before the check answered: `130` for Ctrl-C, `143` for SIGTERM |
288
385
 
289
386
  Only `clean` exits `0`: a status this CLI does not know exits `2`, so a newer
@@ -323,8 +420,21 @@ rewrite them. The remedies name no client, so the CLI adds its own command
323
420
  after some: `slopcop login` when the remedy is to sign in, the listing to
324
421
  choose from after `workspace_not_found` or `workspace_repository_not_found`,
325
422
  and `slopcop whoami` after `repository_disabled`, since the workspace that
326
- disabled it is the one this login is bound to. A failed request prints no
327
- report on stdout.
423
+ disabled it is the one this login is bound to, and `--workspace` after
424
+ `owner_required`. A failed check exits `4` when only a person can clear the
425
+ refusal (`auth_required`, `auth_expired`, `account_required`,
426
+ `repo_not_indexed`, `app_not_installed`, `app_no_repo_access`,
427
+ `gitlab_not_connected`, `no_access`, `insufficient_credits`,
428
+ `repository_disabled`, `repository_add_unconfirmed`,
429
+ `workspace_repository_limit_reached`, `active_repository_limit_reached`,
430
+ `owner_required`, and `browser_required`), so a script can stop instead of
431
+ retrying, and `2` otherwise. Stderr starts with the code, as in `slopcop:
432
+ repository_add_unconfirmed: …`. `human` prints nothing on stdout; `json`
433
+ prints `{"status": "failed", "code": …, "message": …, "remedy": …,
434
+ "retryable": …}`, plus `installUrl` or `topUpUrl` when the service sent one;
435
+ `sarif` prints a run with `slopcop.status` `failed`, whose invocation is
436
+ unsuccessful and carries an `error` notification whose descriptor id is the
437
+ code.
328
438
 
329
439
  ## API
330
440
 
@@ -336,11 +446,16 @@ The CLI calls `POST <service>/v1/check` with a bearer token:
336
446
  "baseCommit": "0123456789abcdef0123456789abcdef01234567",
337
447
  "change": "<base64 git patch>",
338
448
  "selection": {"files": [".slopcop/policies/rule.rqlp"]},
339
- "failOn": "finding"
449
+ "failOn": "finding",
450
+ "addRepository": true
340
451
  }
341
452
  ```
342
453
 
343
- The service rejects any other field. The response carries `status`,
454
+ `addRepository` is sent only after a person confirms adding the repository. The
455
+ service rejects any other field. With `--workspace`, every request, the
456
+ check's, the ignore's and the account routes', carries the workspace's id in
457
+ the `SlopCop-Workspace` header; a name is first looked up with
458
+ `GET <service>/v1/whoami`. The response carries `status`,
344
459
  `baseCommit`, `applied`, `summary`, `findings`, `incomplete`, and `failure`
345
460
  when the analysis did not complete. Against a service from before findings
346
461
  were marked `new`, the CLI treats every finding as new, as that service gated
@@ -414,6 +529,7 @@ Layout:
414
529
  | `internal/service` | `/v1/check` client and the wire contract |
415
530
  | `internal/report` | human, JSON, and SARIF rendering from the structured response |
416
531
  | `internal/config` | stored service URL and token |
532
+ | `internal/hooks` | Claude Code and Codex hook installation and removal |
417
533
  | `npm` | launcher, packer, and tests of the `@brokkai/slopcop` package |
418
534
  | `packaging/pypi` | wheel packer and launcher of the `brokk-slopcop` package |
419
535
  | `skills/slopcop` | the agent skill the binary embeds and installs |
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brokkai/slopcop",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Static analysis for coding agents and CI: check a change through the hosted SlopCop API",
5
5
  "keywords": [
6
6
  "agents",