@synovitec/trellis 0.0.0-stage → 0.6.0-rc.3

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 (50) hide show
  1. package/CHANGELOG.md +406 -0
  2. package/LICENSE +190 -0
  3. package/README.md +206 -2
  4. package/SECURITY.md +105 -0
  5. package/bin/trellis.mjs +5 -0
  6. package/docs/attestation/v0.1.md +96 -0
  7. package/package.json +78 -4
  8. package/schema/trellis.config.schema.json +349 -0
  9. package/src/agents/adapters.mjs +155 -0
  10. package/src/agents/git.mjs +241 -0
  11. package/src/agents/glob.mjs +41 -0
  12. package/src/agents/hooks.mjs +780 -0
  13. package/src/agents/install-adapters.mjs +172 -0
  14. package/src/agents/install.mjs +302 -0
  15. package/src/agents/manifests.mjs +127 -0
  16. package/src/agents/rules.mjs +330 -0
  17. package/src/agents/shell.mjs +253 -0
  18. package/src/agents/state.mjs +233 -0
  19. package/src/agents/transcript.mjs +120 -0
  20. package/src/cli/main.mjs +191 -0
  21. package/src/commands/attest.mjs +208 -0
  22. package/src/commands/checkpoint-state.mjs +62 -0
  23. package/src/commands/doctor.mjs +98 -0
  24. package/src/commands/guide.mjs +62 -0
  25. package/src/commands/init.mjs +131 -0
  26. package/src/commands/key.mjs +31 -0
  27. package/src/commands/scan.mjs +51 -0
  28. package/src/commands/uninstall.mjs +36 -0
  29. package/src/commands/verify.mjs +510 -0
  30. package/src/core/abatty.mjs +25 -0
  31. package/src/core/agents-block.mjs +65 -0
  32. package/src/core/attestation.mjs +213 -0
  33. package/src/core/config.mjs +135 -0
  34. package/src/core/context.mjs +280 -0
  35. package/src/core/diff.mjs +189 -0
  36. package/src/core/evidence.mjs +167 -0
  37. package/src/core/mcp.mjs +51 -0
  38. package/src/index.mjs +11 -0
  39. package/src/mcp/client.mjs +164 -0
  40. package/src/mcp/launch.mjs +57 -0
  41. package/src/mcp/relay.mjs +111 -0
  42. package/src/mcp/server.mjs +210 -0
  43. package/src/mcp/trust.mjs +142 -0
  44. package/src/tools/registry.mjs +370 -0
  45. package/src/tools/roles.mjs +17 -0
  46. package/src/verify/deps.mjs +382 -0
  47. package/src/verify/gitleaks-default.toml +3211 -0
  48. package/src/verify/rules.mjs +177 -0
  49. package/src/verify/secrets.mjs +169 -0
  50. package/src/verify/test-commands.mjs +116 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,406 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package. The format follows Keep a Changelog; versions follow SemVer.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.6.0-rc.3] - 2026-10-31
8
+
9
+ ### Fixed
10
+
11
+ - **The commit checkpoint is installed without gitleaks.** `init --agents` wrote the git hooks only
12
+ where gitleaks was on, so a machine without it got no `trellis verify` on commit and no
13
+ attestation at all, and said nothing. The hooks are now always written, and init says the secret
14
+ scan is off and how to add it.
15
+ - **A secret scan that could not run warns on this machine where the team did not switch gitleaks
16
+ on**, rather than refusing every commit; where `"gitleaks": true` is set, or the team set
17
+ `secrets-scanned` itself, it still blocks. CI, which brings its own pinned gitleaks, blocks as
18
+ before, and a leak found is never softened.
19
+
20
+ ## [0.6.0-rc.2] - 2026-10-30
21
+
22
+ The tenth review, on the code the ninth round added, found the secret scan's own reading of team
23
+ allowlists unsound, and three false refusals. All fixed.
24
+
25
+ ### Security
26
+
27
+ - **The team's gitleaks allowlist is read by gitleaks itself.** Trellis parsed it, and a pattern
28
+ with a character class (`[^/]+\.lock$`) became an empty pattern that allowed every path: a
29
+ secret passed CI. Files are now scanned at their real paths with the team's `.gitleaks.toml`
30
+ and `.gitleaksignore` as gitleaks reads them (`condition = "AND"`, `targetRules`, rule
31
+ allowlists, `[extend] path`), and only gitleaks's built-in global path allowlist is dropped:
32
+ Trellis ships gitleaks 8.30.1's default rules without it (`src/verify/gitleaks-default.toml`,
33
+ MIT, regenerated and checksum-checked by `scripts/gitleaks-rules.mjs`).
34
+ - **A test run's environment.** `COMSPEC` is pinned to the system's cmd.exe, and Cargo, Go, Yarn,
35
+ Python, shell (`BASH_ENV`) and JVM settings that steer a runner are cleared; a test command with
36
+ a line break never counts, and the gateway's `run_tests` runs the accepted command, never the
37
+ raw text; an ignored `.pytest.ini`, `.yarnrc` or `.cargo/config` refuses the record.
38
+
39
+ ### Fixed
40
+
41
+ - **The net change is what lands**: what merging the head into the base gives (`git merge-tree`,
42
+ git 2.38 or later), so criss-cross history no longer reads a change the base made again as the
43
+ pull request reverting it.
44
+ - **A person's test edit is judged as a person's**, file by file: one commit with no attestation
45
+ (the host's "Update branch", a bot) no longer makes every file of the pull request an agent's.
46
+
47
+ ## [0.6.0-rc.1] - 2026-10-30
48
+
49
+ A release candidate: nine independent reviews, the last four adversarial, each fixed in full; the
50
+ tenth is under way. Verify it on a repository of your own before you rely on it.
51
+
52
+ Evidence v2 (design: docs/design/0001-evidence-v2.md). Five independent reviews kept finding the
53
+ same weak point, deciding what a session did from git history; and only Claude Code was held.
54
+ Trellis now holds any agent, and any person, at the commit, and has CI verify every commit again.
55
+
56
+ ### Added
57
+
58
+ - **The commit checkpoint.** `trellis verify` (the pre-commit hook) judges the staged change by
59
+ content: Trellis's own secret scan, each added or moved dependency checked on its registry and
60
+ against OSV at its exact version, and the rules, in two presets (`recommended`, `strict`) grounded
61
+ in published evidence. Blocking rules are the deterministic ones: secrets, dependencies,
62
+ protected tests (an agent may add tests, not change existing ones without a person's approval),
63
+ an unanswered escalation. The rest warn.
64
+ - **The evidence log v2.** One append-only log per repository, each line signed and chained; content
65
+ checks hold the git blob of every file they ran on.
66
+ - **`trellis check tests -- <cmd>`, `trellis escalate`, `trellis approve`** (refused without a
67
+ terminal), **`trellis checkpoint`**.
68
+ - **Attestations.** The post-commit signs an in-toto Statement v1 about each commit and its tree
69
+ (DSSE, ed25519), carried in `refs/notes/trellis` and pushed by the pre-push hook. Format:
70
+ docs/attestation/v0.1.md. **`trellis key [--add]`** commits a developer's public key.
71
+ - **`trellis verify --ci`**, the authority: every commit after a base, its attestation checked, the
72
+ deterministic checks run again, the rules judged again from the change and the recorded
73
+ evidence; a SLSA `verification_summary/v1` and commit checksums for signing. **A GitHub Action**
74
+ (`action.yml`) runs it, and can sign the summary with GitHub artifact attestations.
75
+ - **`trellis mcp`**, the MCP gateway: the checks as tools for any agent (graph_query, run_tests,
76
+ escalate, check_change), and every stdio server in `.mcp.json` relayed through it, with Trellis
77
+ recording each check itself. Dual-era: MCP 2026-07-28 stateless requests and the `initialize`
78
+ handshake of earlier versions. Declared by init in `.mcp.json`, `.cursor/mcp.json`,
79
+ `.codex/config.toml` and `.vscode/mcp.json`.
80
+ - **Cursor, Codex and Copilot hooks**, on the same guard, gates and evidence as Claude Code, each
81
+ in its own format, failing closed.
82
+
83
+ ### Changed
84
+
85
+ - **doctor** shows the checkpoint first; before init, one diagnosis and one command; a tool with
86
+ one known step left warns with that step. init leaves OSV-Scanner off (the checkpoint checks each
87
+ added dependency against OSV).
88
+ - README and SECURITY.md describe v2: the machine is a seatbelt, CI is the authority.
89
+
90
+ ### Security
91
+
92
+ - A commit the session pushed is the session's again. 0.5 left out every commit a remote had,
93
+ so committing and pushing made the work invisible to every rule, a pushed secret included.
94
+ Now only what the remotes held at the start, and what arrived since by a fetch or a pull (read
95
+ from the remote-tracking refs' reflogs), is left out. The guard refuses moving a
96
+ remote-tracking ref by hand, a fetch into one, and rewriting a reflog, which would forge that.
97
+ - AgentShield scans the repository. Without `--path` it scanned the user's `~/.claude`, so
98
+ `agent-setup-scanned` judged plugin caches (never passing on a busy machine) and never looked
99
+ at the repository's changed setup. The Stop hook now runs `scan --path <repo> --format json`,
100
+ reads the report from a file (it can run to megabytes), counts only critical and high findings
101
+ on the setup files the session changed, and treats a report it cannot read, or one about
102
+ another folder, as not scanned rather than clean. `trellis scan` passes `--path .` too.
103
+ - With Trellis gone, a hook blocks instead of crashing. After `git clean -fdx` or
104
+ `npm ci --omit=dev`, the hook's `node` failed with exit 1, which Claude Code ignores, so the
105
+ session ended unchecked. The hook command now checks that Trellis is there and exits 2 (which
106
+ blocks) when it is not. The guard refuses those commands in the first place: `git clean -x`
107
+ or `-X`, an install or prune without devDependencies, and installing `@synovitec/trellis`
108
+ mid-session (another version, or another package, in its place). Existing installs show the
109
+ hooks as outdated; `trellis init --agents` updates them.
110
+ - A graph query counts only when graphify is all that ran (beside a `cd` before it or a
111
+ `| head` after it). `graphify query x; echo NODE fake` printed what an answer looks like and
112
+ unlocked every edit.
113
+ - The agent cannot allowlist its own secret. The Stop hook's gitleaks run ignores inline
114
+ `gitleaks:allow` comments, and when the session touched `.gitleaks.toml` or `.gitleaksignore`
115
+ it scans with gitleaks's default rules instead of them; `GITLEAKS_CONFIG` is not passed on. A
116
+ team allowlist committed before the session still applies.
117
+ - Quotes and escapes no longer hide a path from the guard. `cat $HOME/.trel''lis/key` read the
118
+ signing key: the key, evidence and index-flag checks ran on the raw command text. They now run
119
+ on the words as the shell reads them too.
120
+ - A deleted session record stays missing. A resume or a compaction after
121
+ `find .git/trellis/sessions -delete` started a fresh record, which made everything done before
122
+ it the baseline. The record is now created only when a session starts (`startup`, `clear`), and
123
+ `find` with `-delete`, `-exec` and the like counts as a write on the evidence; the guard now
124
+ covers the whole transcripts folder, not only paths ending in `.jsonl`.
125
+ - A file hidden from git by an ignore rule is a finding. Pointing `core.excludesFile` at a list,
126
+ or writing a `.gitignore` that ignores itself, hid a new file from every rule. The session
127
+ records a fingerprint of the ignore rules at the start; when they changed, every newly ignored
128
+ file under the code, UI, schema or agent-setup paths is reported. A build output the rules
129
+ already ignored is not.
130
+ - `trellis uninstall` and `trellis init` are refused mid-session, by any spelling
131
+ (`npx @synovitec/trellis@x uninstall` included): the first took the hooks out at once, the
132
+ second could rewrite them. Only `trellis hook` was refused before.
133
+ - The test overrides `TRELLIS_STATE_DIR`, `TRELLIS_KEY_FILE` and `TRELLIS_AGENTSHIELD` hold only
134
+ under Node's test runner and outside Claude Code's project, and the guard refuses a command
135
+ that sets one. A session could set them and point the next one at its own key, record or
136
+ scanner.
137
+ - The Stop hook runs AgentShield's npx from an empty temporary folder. Run from the repository,
138
+ npx would take a `node_modules/ecc-agentshield` the session planted there over the pinned
139
+ version; it now fetches that exact version, with npm's integrity check, and still scans the
140
+ repository by its absolute path.
141
+ - A config broken at the start of a session stays the judge. Stop fell back to the current
142
+ config, so an agent could write a permissive one and be judged by it. It now fails closed
143
+ (`config-invalid`) until the person fixes the config.
144
+
145
+ ### Security (reviews six to nine)
146
+
147
+ - **CI judges what lands.** The keys, the rules, the gitleaks allowlist and the test commands are
148
+ read at the base, so a pull request cannot change what judges it. Every file of the net change
149
+ must be what a commit of the range made it, read against every merge base, and the net change
150
+ is judged again as one change against the base: an old ancestor merged in, a criss-cross merge
151
+ keeping an older test, a branch that "adds" a test the base already has or keeps a dependency
152
+ the base upgraded, are all refused. A pull request is judged at its head, never at the merge
153
+ commit a checkout builds; only a checkpoint that passed vouches (`fail` and `unverified` are
154
+ refused).
155
+ - **The workflow is the base's.** The README's workflow runs on `pull_request_target`, so a pull
156
+ request cannot edit the check away; the action runs its own pinned code and a gitleaks checked
157
+ by SHA-256, reads the attestations with the workflow's token (private repositories), fetches a
158
+ fork's, and fails when origin cannot be read. doctor warns about a Trellis step a pull request
159
+ can edit.
160
+ - **The secret scan reads content, not diffs.** Each changed blob is scanned under a neutral name,
161
+ so neither a `.gitattributes` `-diff` line nor gitleaks's built-in path allowlist (`*.pdf`,
162
+ `package-lock.json`, `node_modules/`) hides a secret; a `.gitleaksignore` the change writes is
163
+ never loaded; the team's committed allowlist is applied by Trellis on the real paths; UTF-16 is
164
+ read as text and gzip unpacked; a merge is scanned too.
165
+ - **Dependencies.** Lockfile v1 to v3, aliases by their real name, a `resolved` URL that is not the
166
+ package's own tarball, an integrity that is not npm's, a spec moved to git, pip index, VCS and
167
+ `name @ url` lines, go `replace`, a byte-order mark, and a manifest that does not parse: each is
168
+ checked, or named by the new rule `deps-registry` (warn, block in strict), with registry
169
+ settings, overrides, included requirements files and the manifests and lockfiles Trellis does
170
+ not read yet.
171
+ - **A test run proves a test run.** Only an exact project test command counts (`verify.tests`, or
172
+ read from the project, at the base in CI), run as written, never through the tokens that
173
+ matched it; it counts only for the exact tree it ran on, with an environment cleared of what
174
+ steers a runner and no ignored runner setting present.
175
+ - **tests-protected covers what bends the tests**: runner configs (new ones too), the scripts a
176
+ test script runs, pytest's settings, CI files, the checkpoint's own settings, the agents' hook
177
+ and MCP configs, RSpec specs and test setup files.
178
+ - **Who made a commit is evidence.** Every agent's hooks record its first call of a window; the
179
+ guard refuses clearing `CLAUDECODE` and the person's steps run inside another command.
180
+ - **Merges, rebases and colleagues.** Merge commits are checked and attested; a rebased commit
181
+ keeps its attestation when its change is the same; a commit no pre-commit saw (cherry-pick,
182
+ revert, conflicted rebase) is judged by the post-commit; notes merge line by line on push, so a
183
+ colleague's attestations are never lost.
184
+ - **The MCP gateway starts only what a person trusted** (`trellis mcp trust`, an exact entry and
185
+ the repository files it runs), reads a call's kind from what the server runs, and passes
186
+ arguments intact on Windows.
187
+ - **Hooks fail closed** on every agent when Trellis is missing or crashes; Codex blocks by its
188
+ JSON deny, which survives Windows PowerShell; Codex hooks run on Windows.
189
+ - **Contributors without Trellis**: `verify.unattested: "judge"` judges their commits on their own,
190
+ as an agent's, instead of refusing them.
191
+
192
+ ### Fixed
193
+
194
+ - `init --agents` never writes its pre-commit hook outside the repository. With a machine-wide
195
+ `core.hooksPath`, it wrote there, and every other repository on the machine then failed its
196
+ commits on a path that only exists in this one. It now says so and writes nothing.
197
+ - `graph-before-edit` steps aside when no graph is built, as documented. With graphify installed
198
+ and no `graphify-out/graph.json`, every code edit was refused and no query could unlock it.
199
+ - `doctor` warns when the Postgres MCP reads its URI from a variable that is not set: it showed
200
+ "ok" while the server Claude Code starts would have had no database to read.
201
+ - `trellis scan` reports OSV-Scanner's "no package sources found" (exit 128, a repository
202
+ without a lockfile) as "nothing to scan", not as a failure with no reason given.
203
+
204
+ ## [0.5.0] - 2026-10-27
205
+
206
+ A third, fresh independent review found that 0.4's evidence could still be produced or
207
+ influenced by the agent: its own hook command could be run from the shell to record a check that
208
+ never happened, a crash or a timeout passed the session, a backdated commit escaped the secret
209
+ scan, and any command that exited 0 counted as a scan. This release ties every piece of evidence
210
+ to something the agent does not control.
211
+
212
+ ### Security
213
+
214
+ - Evidence is recorded only for a call Claude Code itself logged: the post-tool hook checks the
215
+ call's id (or, failing that, the same tool and input) in the session's transcript. Running
216
+ `trellis hook` from the shell is refused, and would record nothing anyway.
217
+ - A crash fails closed: the Stop hook sends the agent back once and says the work was not
218
+ checked; the pre-tool hook refuses the call. Before, any exception exited 1, which Claude Code
219
+ treats as a pass.
220
+ - The guard keeps Trellis in place: it refuses uninstalling it, deleting or moving `node_modules`,
221
+ editing its installed code, reading or moving `~/.trellis`, and writing `.git/logs`; matching
222
+ deny rules are added. A change to Trellis's installed code during a session is a finding.
223
+ - AgentShield is run by the Stop hook itself when the agent setup changed, and a clean run is
224
+ recorded with the content it saw; the agent's own `scan || true` proves nothing any more.
225
+ - A graph query counts only when graphify's answer is in the output, and not when the program run
226
+ is a script inside the repository or one put first on the PATH.
227
+
228
+ ### Changed
229
+
230
+ - A session's commits are those reachable from local branches and HEAD's new reflog entries but
231
+ not from where things stood at the start, nor from a remote. Commit dates play no part: a
232
+ backdated commit is scanned, work committed on another branch is judged, and a teammate's work
233
+ pulled mid-session is not blamed on the agent.
234
+ - Without gitleaks installed, the session's commits are reported unscanned; a pre-commit hook is
235
+ no longer trusted on its own (it can be skipped in more ways than a flag shows).
236
+ - The evidence is an append-only log, one signed line per event, so post-tool hooks running in
237
+ parallel no longer lose each other's evidence; the snapshot is written atomically.
238
+ - "Wired" is decided by parsing commands where commands run (package scripts, hooks, shell
239
+ scripts, CI steps read by key, Makefile and justfile recipes, Jenkins `sh` steps). A string in a
240
+ JavaScript file, an `echo`, a step's name or a comment no longer counts.
241
+ - The Stop hook reads a manifest at the start revision only when the file is a manifest, so its
242
+ cost no longer grows with the number of changed files; its budget is 180 s. The post-tool hook
243
+ answers calls that cannot be evidence without reading the transcript.
244
+ - Dependency detection: only the dependency tables of `pyproject.toml` are read (not
245
+ `classifiers`, `keywords` or a linter's `select`); `// indirect` go.mod lines, local paths,
246
+ URLs, git sources and installer upgrades (`pip install -U pip`) are not new dependencies.
247
+ - Session files older than 30 days are removed when a session starts.
248
+ - The OSV-Scanner install hint no longer suggests `@latest`.
249
+
250
+ ### Fixed
251
+
252
+ - The docs no longer claim what the code does not do: `graph-before-edit` only warns at Stop for
253
+ a write made through the shell; one browser check verifies every changed UI file whose current
254
+ content it was taken over, not only those on the page it showed; `init` enables the tools found
255
+ on the machine that runs it.
256
+
257
+ ## [0.4.0] - 2026-10-27
258
+
259
+ A second independent review showed that 0.3's evidence lived where the agent could write it (a
260
+ state file in the temp folder, the transcript, file times, git's index), and that a missing
261
+ record fell back silently to the weak path. This release records evidence as it happens, signs
262
+ it, guards it, and makes the agent look before it writes.
263
+
264
+ ### Added
265
+
266
+ - **Look before you write.** A PreToolUse hook refuses an edit before it happens:
267
+ - `graph-before-edit`: a code file cannot change until the code graph has been asked once this
268
+ session (`graphify query`, `path` or `explain`). One query unlocks every edit. Steps aside
269
+ when the repository has no graph to ask.
270
+ - `schema-before-change`: a migration, model or schema file cannot change until the database
271
+ schema has been looked at with the Postgres MCP.
272
+ Both are checked again at Stop from git, so a write through the shell is caught too.
273
+ - **An evidence ledger**, written by the PostToolUse hook the moment a tool call succeeds: the
274
+ content hash of each changed file when it was seen in the browser or scanned by AgentShield,
275
+ that the graph or the schema was asked, which libraries Context7 was asked about. A UI file is
276
+ verified only when its current content is the content the browser check saw: backdating a
277
+ file or forging a transcript line gains nothing.
278
+ - **`evidence-intact`**, blocking by default: an altered or missing session record, a file
279
+ hidden from git (`update-index --assume-unchanged`, `--skip-worktree`) or an edited
280
+ `.git/info/exclude` is a finding. The changes are still counted.
281
+ - An evidence guard: the PreToolUse hook refuses shell commands that would write the session
282
+ record, the transcripts or `info/exclude`, or set an index flag; `init --agents` adds Claude
283
+ Code deny rules for the same paths and for the signing key.
284
+ - Dependency detection beyond npm: requirements files, pyproject.toml, Cargo.toml, go.mod,
285
+ composer.json and Gemfile, at the root and in workspaces (package.json `workspaces`,
286
+ pnpm-workspace.yaml). A dependency written into any of them counts as added.
287
+ - A global install (`npm i -g @synovitec/trellis`) for repositories without node_modules: the
288
+ hooks and the pre-commit hook then call `trellis` on PATH. Without either, `init` writes no hook
289
+ rather than one that fails on every event.
290
+ - SECURITY.md (with the threat model) and CONTRIBUTING.md.
291
+
292
+ ### Changed
293
+
294
+ - The session record moves into the repository's git folder (`.git/trellis/sessions`) and is
295
+ signed with a per-user key (`~/.trellis/key`).
296
+ - Changed files are read by content, including files hidden by an index flag, files put away in a
297
+ stash, and deletions (a deletion seen in the browser is verified).
298
+ - Secret scanning covers every commit created during the session, on any branch or only in the
299
+ reflog. Without gitleaks, a pre-commit hook is trusted only if no commit skipped it.
300
+ - A tool call counts only when it succeeded; `--help`, `--version`, `command -v`, a heredoc body,
301
+ a shadowing function or alias, and `false && ...` run nothing. A browser check must be on the
302
+ configured origin exactly (scheme, host and port).
303
+ - A Context7 lookup must name the package as a whole word.
304
+ - `config-unchanged` now warns by default and never asks the agent to revert the change: the
305
+ person may have made it, and the session is judged by the start's rules anyway.
306
+ - `init` enables only the tools it finds and proposes the rest with how to install each, so a
307
+ fresh `doctor` is green. AgentShield counts as wired when the agent hooks demand it.
308
+ - Stop has a 120 s budget and gitleaks 40 s, so a slow scan is reported, not cut into a pass.
309
+ - The session brief declares the evidence off limits and still does not say which rules only warn.
310
+
311
+ ### Fixed
312
+
313
+ - Trellis did not recognise the pre-commit hook it writes itself (0.3): `doctor` reported Gitleaks
314
+ unwired right after `init --agents`. The hook now names its script literally.
315
+ - A missing session record, session id or git repository no longer degrades silently: the first
316
+ two block once and say why, the third is reported.
317
+
318
+ ## [0.3.0] - 2026-10-27
319
+
320
+ An independent review showed that 0.2's enforcement could be made inert while looking active.
321
+ This release makes the rules judge evidence and fail closed.
322
+
323
+ ### Security
324
+
325
+ - Every command now names the scoped package (`npx @synovitec/trellis`). The bare `npx trellis`
326
+ that 0.2's README, AGENTS.md block and rule messages printed resolves to an unrelated package
327
+ on the registry when this one is not installed, and npx would have downloaded and run it.
328
+ - The hooks call the installed script with node (`node "$CLAUDE_PROJECT_DIR/node_modules/..."`),
329
+ never through npx. The generated pre-commit hook does the same, and says how to remove itself
330
+ when the package is gone instead of failing with an npx error.
331
+ - `trellis scan` runs AgentShield pinned (`ecc-agentshield@1.6.0`), not whatever is newest.
332
+
333
+ ### Changed
334
+
335
+ - Changed files are read from git, not from the transcript: a snapshot at session start (HEAD
336
+ and the dirty files, by content), compared at stop. A write through the shell, an MCP editor or
337
+ a subagent counts like one through the Edit tool.
338
+ - Subagent transcripts (`<session>/subagents/*.jsonl`) are read: their checks and their edits
339
+ are the session's.
340
+ - A rule is met by a real run, parsed as commands, after the change, by time: `echo gitleaks`,
341
+ a comment, `gitleaks version`, `browser_close` and a scan run before the change no longer count.
342
+ A browser check is a navigation to the configured origin followed by a snapshot or screenshot.
343
+ A Context7 lookup must name the added package; a dependency written into package.json by hand
344
+ counts as added. MCP tools are recognised under plugin and connector names too.
345
+ - `secrets-scanned`: the Stop hook runs gitleaks itself over the session's commits.
346
+ - `graph-first` can only warn: sending an agent back after the work is done cannot fix it, and
347
+ a grep over a command's output (`npm test | grep fail`) is no longer a file search.
348
+ - Blocks are counted per session (`maxBlocks`, default 2), then the person decides. Claude
349
+ Code's `stop_hook_active` is no longer trusted, since any other Stop hook sets it.
350
+ - The session brief lists what will be checked, not which rules only warn.
351
+ - `doctor` matches runs, not mentions (comments are ignored), reads Makefile, justfile,
352
+ Jenkinsfile and more CI files, and reports a hook whose script is not installed as broken.
353
+ - The default UI paths cover any folder (monorepos) and leave out tests, stories and build output.
354
+ - abatty is an optional peer dependency: Trellis runs the project's own copy.
355
+
356
+ ### Added
357
+
358
+ - `config-unchanged`: trellis.config.json edited during a session is a finding, and the session
359
+ is judged against the rules as they were when it started. It blocks unless set otherwise.
360
+ - A broken config, or a transcript that cannot be read, is reported, never a silent pass.
361
+ - The hook finds the project from `CLAUDE_PROJECT_DIR`, else the nearest folder holding the config.
362
+ - `trellis uninstall`: the hooks, Trellis's pre-commit hook and the AGENTS.md block come out; the
363
+ config and MCP servers stay.
364
+ - `init` upgrades hook entries written by an older version, and uses the hooks folder git really
365
+ runs (core.hooksPath, worktrees).
366
+
367
+ ## [0.2.0] - 2026-10-27
368
+
369
+ ### Added
370
+
371
+ - Agent hooks, so a coding agent uses the tools systematically rather than when it remembers to.
372
+ `trellis init --agents` adds three Claude Code hooks to `.claude/settings.json` (beside the
373
+ team's own, never replacing one) and, when there is none, a pre-commit hook that runs the
374
+ secret scan for every agent and every person.
375
+ - `trellis hook session-start` hands the agent the routing, what is broken here, and the rules
376
+ it will be held to.
377
+ - `trellis hook post-tool` reminds it once, at the third file search with no graph query, to
378
+ ask the code graph.
379
+ - `trellis hook stop` reads the session transcript and judges what was done, not what was
380
+ claimed. Five rules, each active only when its tool is enabled: `ui-verified` (a UI edit is
381
+ followed by a browser check), `agent-setup-scanned` (an agent config edit by an AgentShield
382
+ run), `docs-looked-up` (a new dependency by a Context7 lookup), `graph-first` (many searches
383
+ by a graph query), `secrets-scanned` (a commit by a secret scan, unless a git hook runs one).
384
+ - `agents` in `trellis.config.json`: `mode` (`block` sends the agent back with the reason,
385
+ `warn` tells the person, `off`), per-rule overrides, and the UI and agent-setup path globs.
386
+ `init --agents` starts in `warn`. A stop already sent back once is never blocked again, so a
387
+ session cannot loop.
388
+ - `trellis doctor` reports agent rules that are configured but have no hook to run them.
389
+
390
+ ## [0.1.0] - 2026-10-27
391
+
392
+ ### Added
393
+
394
+ - `trellis init`: writes `trellis.config.json` from the stack it finds, adds the MCP servers
395
+ (Context7, Playwright MCP, Postgres MCP) to `.mcp.json` without touching existing entries, puts
396
+ the tool routing in a managed block of `AGENTS.md`, and git-ignores the tools' output. Run
397
+ twice, it changes nothing. `--dry-run` writes nothing.
398
+ - `trellis doctor`: every enabled tool as ok, warn, unwired (installed, but nothing calls it) or
399
+ missing, with the command that fixes each. `--json` for machines, `--strict` fails on warnings.
400
+ - `trellis guide [tool|role]`: which tool answers which question, its rule, and its state here.
401
+ - `trellis scan`: runs Gitleaks, OSV-Scanner and AgentShield. A scanner that could not run fails
402
+ the scan unless `--allow-missing`; `--history` scans the whole git history.
403
+ - `trellis gate|explain|prove|ratchet|status|rules`: abatty's commands, run as they are.
404
+ - Twelve tools across four roles: AgentShield (frame); graphify, Context7, Postgres MCP, Find
405
+ Skills (guide); ECC, Playwright MCP (help); abatty, Playwright E2E, Gitleaks, OSV-Scanner,
406
+ Lighthouse (guard).
package/LICENSE ADDED
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 Synovitec
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.