amont 1.25.1 → 1.27.0

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 (2) hide show
  1. package/README.md +144 -266
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -29,89 +29,28 @@ How that stacks up against pre-commit, lefthook and husky, feature by feature:
29
29
 
30
30
  ## Install
31
31
 
32
- **Linux and macOS**
33
-
34
32
  ```sh
33
+ # Linux and macOS
35
34
  curl -fsSL https://raw.githubusercontent.com/fredericrous/amont/main/install/install.sh | sh
36
35
  ```
37
36
 
38
- **Windows** (PowerShell — the line above is POSIX `sh` and only reaches
39
- Windows through Git Bash)
40
-
41
37
  ```powershell
38
+ # Windows (the line above is POSIX sh and only reaches Windows through Git Bash)
42
39
  irm https://raw.githubusercontent.com/fredericrous/amont/main/install/install.ps1 | iex
43
40
  ```
44
41
 
45
42
  Either one downloads a release binary, verifies it against the published
46
- `SHA256SUMS`, and puts it where the hooks already look `~/.local/bin`, or
47
- `%USERPROFILE%\.local\bin`. Both **enable nothing**: hooks are turned on per
48
- repository, by you, afterwards.
43
+ `SHA256SUMS`, and puts it where the hooks already look. Both **enable
44
+ nothing** hooks are turned on per repository, by you, afterwards:
49
45
 
50
46
  ```sh
51
47
  cd <your-repo> && amont install # this repository only
52
48
  amont list # what would run here, and why not
53
49
  ```
54
50
 
55
- Prefer not to pipe a script into a shell? Download a binary and its checksum
56
- from [Releases](https://github.com/fredericrous/amont/releases/latest)
57
- prebuilt for Linux (gnu/musl, x86_64 and aarch64), macOS (Intel and Apple
58
- silicon) and Windows. Or build from source: `cargo build --release`.
59
-
60
- From [crates.io](https://crates.io/crates/amont):
61
-
62
- ```sh
63
- cargo install amont
64
- ```
65
-
66
- Or with Homebrew:
67
-
68
- ```sh
69
- brew tap fredericrous/tap
70
- brew trust fredericrous/tap # Homebrew asks this of every third-party tap
71
- brew install amont
72
- ```
73
-
74
- **In a JavaScript project**, the binary can travel with the repository rather
75
- than with the machine, so a teammate who clones it needs no install step at all:
76
-
77
- ```sh
78
- npm i -D amont # or: pnpm add -D amont
79
- npm pkg set scripts.prepare="amont init"
80
- npm install # the hooks appear
81
- ```
82
-
83
- Six prebuilt platform packages are declared as `optionalDependencies`, so npm
84
- installs exactly one and runs no install scripts at all — this survives
85
- `npm ci --ignore-scripts`. `amont init` wires up that one repository and
86
- nothing else: no `~/.local/bin`, no template directory, no prompts.
87
-
88
- If the project also installs **without** its dev dependencies anywhere — `npm ci
89
- --omit=dev`, the usual second stage of a Dockerfile — write
90
- `"prepare": "amont init || true"` there, since `prepare` still runs and `amont`
91
- will not be installed. [The details](docs/install.md#if-anything-ever-installs-without-your-dev-dependencies).
92
-
93
- ## Uninstall
94
-
95
- ```sh
96
- amont uninstall # this repository
97
- amont uninstall --binary # …and remove ~/.local/bin/amont too
98
- ```
99
-
100
- Uninstall is listed second on purpose. These hooks can block a commit, so the
101
- honest question to answer first is how you get out — and the answer is that
102
- `uninstall` removes our five shims and **nothing else**. A hook you wrote
103
- yourself is left where it is and named in the output, whatever it is; a hook it
104
- cannot even read is named too rather than passed over in silence. Your
105
- `hook.skip` and `amont.severity` settings are never touched, because those
106
- are your statements about your repository.
107
-
108
- This is also why the README does not tell you to run
109
- `rm $(git rev-parse --git-dir)/hooks/*`. That glob deletes every hook in the
110
- directory — including ones other tools installed and ones you wrote — to remove
111
- five files that belong to us.
112
-
113
- To bypass a single run rather than uninstall: `git commit --no-verify`.
114
- To turn off one check permanently, see [Turning a check off, or down](#turning-a-check-off-or-down).
51
+ Homebrew, crates.io, npm, prebuilt binaries for six targets, building from
52
+ source, and what `--force` will and will not do:
53
+ [installing and activating](docs/install.md).
115
54
 
116
55
  ## What actually runs
117
56
 
@@ -136,210 +75,155 @@ pre-push
136
75
  ● runs here ○ inert ⊘ skipped via hook.skip ✗ declaration unusable
137
76
  ```
138
77
 
139
- Thirty-seven built-in checks across five git hooks, plus any your repository declares
140
- itself. The full list, with what each one needs before it fires, is in
78
+ Thirty-seven built-in checks across five git hooks, plus any your repository
79
+ declares itself. What each one needs before it fires:
141
80
  [the checks reference](docs/checks.md).
142
81
 
143
- ## Two ways to turn hooks on
82
+ ## Turning hooks on
144
83
 
145
- **Per repository** the default, and nothing runs anywhere you did not ask:
84
+ **Per repository** is the default, and nothing runs anywhere you did not ask:
146
85
 
147
86
  ```sh
148
87
  cd <your-repo> && amont install
149
88
  amont-fleet install --root ~/Developer # or in bulk, across many repos
150
89
  ```
151
90
 
152
- `amont install --force` replaces a hook the installer would otherwise
153
- refuse — one carrying no marker of ours, or a symlink. Even `--force` never
154
- writes a tracked file or a directory. The full semantics:
155
- [what `--force` will and will not do](docs/install.md#--force-and-what-it-will-not-do).
156
-
157
- **Everywhere, forever** — an opt-in, and a real one:
91
+ **Everywhere, forever** is an opt-in, and a real one:
158
92
 
159
93
  ```sh
160
94
  amont enroll --conventions declared
161
95
  ```
162
96
 
163
- One command per machine: every future `git clone` and `git init` arrives
164
- with the hooks. `--conventions declared` keeps the house rules (commit
165
- shapes, branch names, gates) scoped to repositories that commit an
166
- `amont.conf`, while the safety net conflict markers, secrets, oversized
167
- files, debug leftovers runs everywhere. That split is what makes the
168
- grant safe on a machine that also clones other people's projects, and it
169
- is the backbone of [rolling amont out to a team](docs/team-rollout.md).
170
- The manual spelling:
171
-
172
- ```sh
173
- git config --global init.templateDir ~/.config/git/git-templates/templates
174
- ```
175
-
176
- Git copies that directory into `.git/hooks` on every `init` **and every
177
- clone**, so from then on every repository you clone runs these hooks without
178
- being asked again. That is the convenience, and it is worth having.
179
-
180
- It is also a standing grant, so it is worth stating what you granted. A cloned
181
- repository can declare its own checks in `amont.conf`, and with this key
182
- set those are one `amont trust` away from running on your first commit in a
183
- repository you may have cloned only to read. If you set this, **trust
184
- deliberately** rather than letting installation be the moment you decided.
185
-
186
- ## One view across every repo
97
+ One command per machine: every future `git clone` and `git init` arrives with
98
+ the hooks. `--conventions declared` keeps the house rules commit shapes,
99
+ branch names, gates scoped to repositories that commit an `amont.conf`,
100
+ while the safety net of conflict markers, secrets, oversized files and debug
101
+ leftovers runs everywhere.
187
102
 
188
- `amont-fleet` installed separately, on purpose answers the questions a
189
- directory full of repositories accumulates: which repos are covered, which
190
- shims went stale after an upgrade, what every check is doing where, and which
191
- repository is quietly carrying a `hook.skip` somebody forgot.
192
-
193
- ![the amont-fleet dashboard scanning a fleet of repositories](docs/assets/fleet-demo.gif)
194
-
195
- ```sh
196
- amont-fleet install --root ~/Developer # shims into every repo at once
197
- amont-fleet # report the fleet
198
- amont-fleet tui # the dashboard above
199
- ```
200
-
201
- Design record: [the fleet dashboard](docs/fleet-dashboard.md).
103
+ That split is what makes the grant safe on a machine that also clones other
104
+ people's projects. It is still a standing grant, so it is worth stating what
105
+ you granted: a cloned repository's own checks are then one `amont trust` away
106
+ from running on your first commit in a repository you may have cloned only to
107
+ read. **Trust deliberately** rather than letting installation be the moment
108
+ you decided. The full reasoning, and rolling this out to a team:
109
+ [team rollout](docs/team-rollout.md).
202
110
 
203
111
  ## Trust: a repository you clone cannot run its own checks
204
112
 
205
113
  `amont.conf` is committed — that is the point, a team shares a check by
206
- committing it. The consequence is that cloning a repository and committing to
207
- it would otherwise run commands that repository chose, and neither of those
208
- acts is one anybody performs *as a decision about trust*.
209
-
210
- So a cloned repository's declared checks are **inert until you say otherwise**:
114
+ committing a line rather than by everybody installing something. So a
115
+ repository you cloned can _declare_ checks, and running them is a decision you
116
+ make, not one `git clone` makes for you.
211
117
 
212
118
  ```sh
213
- amont trust # show what this repo declares, and accept it
214
- amont trust --show # what is trusted here
119
+ amont trust # show what it declares, then record it
120
+ amont trust --show # what it declares, and whether it is trusted
215
121
  amont trust --revoke
216
122
  ```
217
123
 
218
- The record is keyed on the file's **content**, not its path, so a `git pull`
219
- that adds a command does not inherit the consent given to the file before it.
220
- The fingerprint is `git hash-object --no-filters`, and the `--no-filters` is
221
- not decoration: plain `hash-object` applies the clean filter and eol conversion
222
- that the repository's own committed `.gitattributes` asks for, which would let
223
- a repository choose the transform its own consent is taken through.
224
-
225
- Full reasoning: [the trust model](docs/trust.md).
124
+ Until then the declarations are **reported, not dropped** you can see there
125
+ is a decision waiting. Acceptance is recorded against the file's _content_, so
126
+ a `git pull` that adds a command does not inherit the trust you gave the file
127
+ before it. [The trust model](docs/trust.md).
226
128
 
227
- ## Turning a check off, or down
129
+ ## Custom checks, and packs
228
130
 
229
- Every check has an id, `<trigger>-<name>` `pre-commit-clippy`. Three things
230
- name it, and both config surfaces read all three the same way:
131
+ A repository declares checks of its own in a committed `amont.conf` five
132
+ whitespace-separated fields, no shell:
231
133
 
232
- ```sh
233
- git config --add hook.skip pre-commit-clippy # that one check
234
- git config --add hook.skip clippy # that check, on either trigger
235
- git config --add hook.skip pre-commit # every pre-commit check
236
134
  ```
237
-
238
- `amont.severity.<key>` takes the same three, and keeps the signal — the
239
- check still runs and still reports, it just stops failing the commit:
240
-
241
- ```sh
242
- git config amont.severity.clippy warn
243
- git config amont.severity.pre-commit warn
135
+ # stage name scope severity command
136
+ pre-commit lint-shell *.sh block scripts/lint-shell.sh
137
+ pre-commit rubocop *.rb+.rubocop.yml block rubocop
138
+ pre-push smoke * warn make smoke
244
139
  ```
245
140
 
246
- Where several keys reach one check the most specific wins: full id, then short
247
- name, then trigger. Nothing matches by substring `hook.skip e` reaches
248
- nothing at all, and skipping `lint-js` leaves `lint-json-yaml` alone.
141
+ They run alongside the built-ins, obey the same `hook.skip` and
142
+ `amont.severity` controls, and are inert until trusted.
249
143
 
250
- A skipped check is announced on every commit, so a config line nobody remembers
251
- writing cannot go on silently disabling things. More in
252
- [opting out](docs/opting-out.md) and [configuration](docs/configuration.md).
144
+ The `+` in the scope column splits _what the change touches_ from _what the
145
+ repository carries_: `*.rb+.rubocop.yml` reads "a staged `.rb`, **and** this
146
+ repository has a `.rubocop.yml`". That is what lets a check be safe to hand to
147
+ somebody else.
253
148
 
254
- ## Making the commit convention yours
149
+ ### Shipping one packs
255
150
 
256
- `commit-msg` is the one hook `hook.skip` and `amont.severity` cannot reach, so its
257
- opinions are adjustable in themselves:
151
+ A **pack** is any git repository with an `amont.pack` at its root, written in
152
+ that same syntax. `amont add` vendors its rows into your `amont.conf`:
258
153
 
259
154
  ```sh
260
- amont setup # four questions, current values as the defaults
155
+ amont add github:fredericrous/amont-pack-java@v1
261
156
  ```
262
157
 
263
- It asks where the type's gitmoji goes `none` (the default: your subject,
264
- untouched), `prefix`, `suffix` or `replace` and for the subject limit, the
265
- description budget and the body wrap column. Then it prints the exact
266
- `git config` lines it wrote, so you can paste them into your dotfiles or hand
267
- them to a teammate.
158
+ What ships is **text, not execution**. The rows land between markers with the
159
+ pack's commit id beside them, the manifest's fingerprint changes, and every
160
+ declared check is inert until you `amont trust` it. Adding a pack is never the
161
+ moment anything becomes runnable.
268
162
 
269
- ```sh
270
- git config amont.commit.gitmoji suffix # feat: add a cart
271
- git config amont.commit.descriptionMax 68 # still fits a 72-col subject
272
- git config amont.commit.bodyWrap 0 # leave my stack traces alone
273
- ```
274
-
275
- The limits measure what you wrote, so a gitmoji never eats your budget, and
276
- re-running over an amended message changes nothing. Full reference:
277
- [commit conventions](docs/commit-convention.md#if-the-defaults-do-not-fit).
163
+ Want to write one? [`fredericrous/amont-pack-java`](https://github.com/fredericrous/amont-pack-java)
164
+ is a complete worked example two rows, one for Maven and one for Gradle, so
165
+ half of it is visibly inert wherever you install it. Its README is the
166
+ long-form authoring guide: gating your rows, what a pack may not carry, and
167
+ how to publish and tag one.
278
168
 
279
- ## Custom checks
169
+ Full reference for both: [custom checks](docs/custom-checks.md).
280
170
 
281
- A repository can declare checks of its own in a committed `amont.conf`:
171
+ ## Day to day
282
172
 
283
- ```
284
- # stage name scope severity command
285
- pre-commit shellcheck *.sh block scripts/lint-shell.sh
286
- pre-push smoke * warn make smoke
173
+ ```sh
174
+ amont run # would my commit pass? (the staged set)
175
+ amont run --all-files # does my working tree pass? (git ls-files)
176
+ amont check src/main.rs # what is wrong with these FILES — no index, no
177
+ # staging; `file:line: message` an editor can parse
178
+ amont list # what would run here, and why not
179
+ amont restore # bring back unstaged work a killed hook parked
287
180
  ```
288
181
 
289
- They run alongside the built-ins, obey the same `hook.skip` and
290
- `amont.severity` controls, and are inert until trusted. Full reference:
291
- [custom checks](docs/custom-checks.md).
182
+ `amont run --all-files` on a dirty tree reports on content that is not
183
+ committed and may never be which is what you want when adopting a check into
184
+ an existing repository, where `git add .` is not an acceptable way to measure
185
+ the mess.
292
186
 
293
- ## Running the checks yourself
187
+ Turning one off, or down:
294
188
 
295
189
  ```sh
296
- amont run # would my commit pass? (the staged set)
297
- amont run --all-files # does my working tree pass? (git ls-files)
298
- amont run pre-commit-prettier
299
- amont list # what would run here, and why not
190
+ git config amont.severity.clippy warn # runs, reports, does not block
191
+ git config hook.skip clippy # does not run at all
300
192
  ```
301
193
 
302
- The two questions are different on purpose. `--all-files` on a dirty tree
303
- reports on content that is not committed and may never be — which is what you
304
- want when adopting a check into an existing repository, where `git add .` is
305
- not an acceptable way to measure the mess.
194
+ Prefer the downgrade: it keeps the check working and keeps you looking at what
195
+ it finds. [Opting out](docs/opting-out.md) · [configuration](docs/configuration.md)
196
+ · [commit conventions](docs/commit-convention.md).
306
197
 
307
- ## What a push actually tests
198
+ **What a push actually tests.** By default `pre-push` runs your suite against
199
+ the _working tree_, and says so — fast, and usually what you want, but not what
200
+ you are pushing. `git config amont.testPushedTree true` runs it in a throwaway
201
+ checkout of the commits being pushed instead.
308
202
 
309
- By default `pre-push` runs your suite against the **working tree**, and says
310
- so. That is fast and usually what you want, but it is not what you are pushing:
311
- an uncommitted fix makes a broken commit look green.
203
+ **For coding agents.** `amont list --json` is the same answer as `amont list`,
204
+ machine-readable: declared and effective severity, whether each check fires
205
+ here and why not, and the command if it is a declared external. `--stage`
206
+ filters to one trigger, `--pushed` scopes to what your next push would carry.
207
+ `amont agents-md` writes the guidance block into `AGENTS.md`.
208
+ [Where the hooks fit in your flow](docs/coding-flow.md).
312
209
 
313
- ```sh
314
- git config amont.testPushedTree true
315
- ```
316
-
317
- turns on the accurate answer — the suite runs in a throwaway checkout of the
318
- commits being pushed, and your tree is not touched. It costs a second checkout
319
- and a build that cannot reuse your `target/` cache, which is why it is opt-in.
210
+ ## One view across every repo
320
211
 
321
- ## For coding agents
212
+ `amont-fleet` installed separately, on purpose — answers the questions a
213
+ directory full of repositories accumulates: which repos are covered, which
214
+ shims went stale after an upgrade, and which repository is quietly carrying a
215
+ `hook.skip` somebody forgot.
322
216
 
323
- `amont list --json` is the same answer as `amont list`, machine-readable:
324
- every check's declared and effective severity, whether it fires here and why
325
- not, and its command if it is a declared external. `--stage` filters to one
326
- trigger; `--pushed` scopes to what your *next push* would carry (`@{u}..HEAD`)
327
- rather than the whole tracked tree.
217
+ ![the amont-fleet dashboard scanning a fleet of repositories](docs/assets/fleet-demo.gif)
328
218
 
329
219
  ```sh
330
- amont list --json --stage pre-push --pushed
331
- amont agents-md # write a self-verifying pointer into AGENTS.md
332
- amont agents-md --check # exit non-zero if it has drifted
220
+ amont-fleet install --root ~/Developer # shims into every repo at once
221
+ amont-fleet # report the fleet
222
+ amont-fleet tui # the dashboard above
223
+ amont-fleet fix --root ~/Developer # what drifted (dry run)
333
224
  ```
334
225
 
335
- `agents-md` only ever touches a `<!-- amont:start -->` / `<!-- amont:end -->`
336
- span, so the rest of the file stays yours.
337
-
338
- The block it writes also warns the agent that `git commit` and `git push`
339
- run their checks first — pre-commit can mean clippy building a workspace,
340
- pre-push a whole test suite — and an agent whose shell tool defaults to a
341
- two-minute timeout will kill the command mid-check and read its own
342
- impatience as a failure. Ten minutes is the safe floor, for both.
226
+ Design record: [the fleet dashboard](docs/fleet-dashboard.md).
343
227
 
344
228
  ## Why you can let this near your commits
345
229
 
@@ -347,68 +231,61 @@ A prompt theme is cosmetic. This blocks commits and pushes, reads every staged
347
231
  file, and runs with your credentials while nobody is watching — so the claim it
348
232
  has to earn is not "delightful", it is "harmless".
349
233
 
350
- - **The commit path links no external crates.** `amont` and
351
- `amont-runtime` are std-only, and `scripts/check-no-deps.sh` fails a build
352
- that changes that — fails *closed*, so a cargo error or an unreachable
353
- registry is a failure rather than a reassuring green tick. `amont-fleet`
354
- takes dependencies quite happily; it is installed separately and runs when
355
- asked.
356
- - **No network, ever.** The binary phones nothing home no telemetry, no
357
- update checks, no fetches. With the commit path std-only, there is not even
358
- an HTTP client linked to do it with.
359
- - **Over six hundred tests**, run on Linux, macOS and Windows, alongside `cargo fmt
360
- --check`, `clippy -D warnings`, an MSRV floor of 1.74 compiled for the commit
361
- path, and `cargo-audit`.
234
+ - **The commit path links no external crates.** `amont` and `amont-runtime`
235
+ are std-only, and `scripts/check-no-deps.sh` fails a build that changes
236
+ that — fails _closed_, so a cargo error or an unreachable registry is a
237
+ failure rather than a reassuring green tick.
238
+ - **No network, ever.** No telemetry, no update checks, no fetches. With the
239
+ commit path std-only there is not even an HTTP client linked to do it with.
240
+ - **Over a thousand tests**, run on Linux, macOS and Windows, alongside
241
+ `cargo fmt --check`, `clippy -D warnings`, an MSRV floor of 1.74 compiled
242
+ for the commit path, and `cargo-audit`.
362
243
  - **v1.0.0 followed a full security review**, and each finding landed with a
363
- committed reproduction: a drive-by RCE via a relative path in the shim; a
364
- held-store format that let a repository delete a tracked file and plant a
365
- symlink outside the worktree; a trust prompt a repository could conceal
366
- declarations from; guards that wrote through symlinks onto tracked source; a
367
- commit subject shaped like a trailer destroying commit trailers;
368
- `pre-commit-pyright` blocking every commit it ran on.
244
+ committed reproduction a drive-by RCE via a relative path in the shim, a
245
+ held-store format that let a repository plant a symlink outside the
246
+ worktree, a trust prompt a repository could conceal declarations from.
369
247
  - **Your uncommitted work is the thing that must never be lost.** The release
370
- profile deliberately omits `panic = "abort"` so that the `Drop` that restores
248
+ profile deliberately omits `panic = "abort"` so the `Drop` that restores
371
249
  unstaged work still runs when a check panics — with a test asserting on the
372
- manifest, because cargo ignores that setting for test targets and no
373
- behavioural test could catch the regression.
250
+ manifest, because no behavioural test could catch that regression.
374
251
 
375
252
  Threat model and private reporting: [SECURITY.md](SECURITY.md).
376
253
 
377
- ## Windows
378
-
379
- Everything works, and the PowerShell one-liner in [Install](#install) is all
380
- the setup most machines need. One difference: there is no symlink. To build
381
- from source instead — Git for Windows ships `bash` and coreutils but not
382
- `make`:
383
-
384
- ```sh
385
- cargo build --release
386
- ./target/release/amont install
387
- ```
388
-
389
- On macOS/Linux `~/.config/git/git-templates` is usually a symlink to the
390
- checkout, so `init.templateDir` can point at a stable XDG path. Windows does
391
- not create symlinks without Developer Mode or elevation, so point git straight
392
- at the checkout instead:
393
-
394
- ```sh
395
- git config --global init.templateDir 'C:/path/to/amont/templates'
396
- ```
397
-
398
- Nothing else changes. The shims never need the symlink: they resolve the binary
399
- at runtime, trying `$GIT_HOOKS_BIN`, the baked path, `~/.local/bin/amont`
400
- and `~/.local/bin/amont.exe`, then `PATH`.
401
-
402
254
  ## Requirements
403
255
 
404
- - **Git 2.31+** — `git rev-parse --path-format=absolute` landed there, and
405
- three places depend on it. On an older git those return nothing rather than
406
- failing loudly, which is the worst shape for a version floor: the tool
407
- appears to work and quietly resolves the wrong paths.
256
+ **Git 2.31+** — `git rev-parse --path-format=absolute` landed there, and three
257
+ places depend on it. On an older git those return nothing rather than failing
258
+ loudly, which is the worst shape for a version floor.
408
259
 
409
260
  The hooks are a single binary with no runtime dependencies. Each check brings
410
261
  its own tool requirement only where you have opted into that check.
411
262
 
263
+ Everything works on Windows; the one difference is that there is no symlink,
264
+ so `init.templateDir` points straight at the checkout. [Details](docs/install.md).
265
+
266
+ ## The repositories around it
267
+
268
+ Three companions, each its own repository because it runs somewhere amont
269
+ deliberately does not:
270
+
271
+ - [**amont-agent**](https://github.com/fredericrous/amont-agent) — a Claude
272
+ Code `PreToolUse` hook for the mistake no git hook can reach, because it
273
+ lives in the command string itself: `git push … | tail -5` reports tail's
274
+ exit status, so a rejected push reads as success. The guard judges the
275
+ pipeline before it runs. Independent by design — no shared code, and
276
+ neither needs the other; they meet in one optional place, where its
277
+ session notice asks `amont agents-md --check` whether the guidance block
278
+ an agent is about to believe has gone stale.
279
+ - [**attest**](https://github.com/fredericrous/attest) — the CI half of
280
+ `amont.attest`: when every pre-push block gate passed locally, amont
281
+ leaves a **signed** note on the tree it tested, and this single-purpose
282
+ verifier lets CI skip work provably already done. Fail-open by
283
+ construction, and separate precisely so that amont itself never runs in
284
+ CI — [the reasoning](docs/ci.md).
285
+ - [**amont-pack-java**](https://github.com/fredericrous/amont-pack-java) —
286
+ the worked example of [a pack](#shipping-one--packs): how checks amont
287
+ deliberately does not build in get shipped anyway.
288
+
412
289
  ## Documentation
413
290
 
414
291
  The full documentation is in [`docs/`](docs/), versioned with the code and
@@ -417,9 +294,10 @@ published as a book:
417
294
  - [Installing and activating](docs/install.md)
418
295
  - [The checks](docs/checks.md) · [Configuration](docs/configuration.md) ·
419
296
  [Opting out](docs/opting-out.md)
420
- - [The trust model](docs/trust.md) · [Custom checks](docs/custom-checks.md)
297
+ - [The trust model](docs/trust.md) · [Custom checks and packs](docs/custom-checks.md)
421
298
  - [Where the hooks fit in your flow](docs/coding-flow.md) ·
422
299
  [Commit conventions](docs/commit-convention.md) ·
300
+ [CI, and why amont does not run in it](docs/ci.md) ·
423
301
  [How it compares](docs/similar-projects.md) ·
424
302
  [Ideas, not a roadmap](docs/ideas.md)
425
303
  - Decision records for maintainers: [hook architecture](docs/hook-architecture.md),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amont",
3
- "version": "1.25.1",
3
+ "version": "1.27.0",
4
4
  "description": "Opinionated git hooks that judge what you are committing, not what is on disk",
5
5
  "keywords": [
6
6
  "git",
@@ -32,11 +32,11 @@
32
32
  "node": ">=18"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@amont-hooks/darwin-arm64": "1.25.1",
36
- "@amont-hooks/darwin-x64": "1.25.1",
37
- "@amont-hooks/linux-arm64-gnu": "1.25.1",
38
- "@amont-hooks/linux-x64-gnu": "1.25.1",
39
- "@amont-hooks/linux-x64-musl": "1.25.1",
40
- "@amont-hooks/win32-x64": "1.25.1"
35
+ "@amont-hooks/darwin-arm64": "1.27.0",
36
+ "@amont-hooks/darwin-x64": "1.27.0",
37
+ "@amont-hooks/linux-arm64-gnu": "1.27.0",
38
+ "@amont-hooks/linux-x64-gnu": "1.27.0",
39
+ "@amont-hooks/linux-x64-musl": "1.27.0",
40
+ "@amont-hooks/win32-x64": "1.27.0"
41
41
  }
42
42
  }