@agenttrail/guardrails 0.0.2 → 0.2.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.
@@ -3,10 +3,10 @@ export { Action, FixtureInput, Fixtures, Match, RuleInput, Severity } from './sc
3
3
  import 'zod';
4
4
 
5
5
  /**
6
- * The eight packs, and the order they are presented in.
6
+ * The eleven packs, and the order they are presented in.
7
7
  *
8
8
  * These ids are authoritative and shared with the rules authored against them,
9
- * and with the guard's `config.json` `enabledPacks`. Renaming one of these is a
9
+ * and with the guard's `config.json` `disabledPacks`. Renaming one of these is a
10
10
  * breaking change to a user's config file.
11
11
  *
12
12
  * Working-tree destruction leads: destructive git commands are the most frequently
@@ -16,10 +16,10 @@ import 'zod';
16
16
  * Per-pack rule counts are deliberately NOT encoded here: a rule count asserted in
17
17
  * code becomes a reason to write a weak rule to hit a number.
18
18
  */
19
- declare const PACKS: readonly ["working-tree", "destructive-data", "prod-infra", "secret-exposure", "rce-supply-chain", "safety-bypass", "privilege-supply-chain", "file-scope"];
20
- /** One of the eight pack ids. A rule's `category` is always one of these. */
19
+ declare const PACKS: readonly ["working-tree", "destructive-data", "prod-infra", "secret-exposure", "rce-supply-chain", "safety-bypass", "privilege-supply-chain", "file-scope", "agent-context", "test-integrity", "exfiltration"];
20
+ /** One of the eleven pack ids. A rule's `category` is always one of these. */
21
21
  type Pack = (typeof PACKS)[number];
22
- /** Is this string one of the eight pack ids? */
22
+ /** Is this string one of the eleven pack ids? */
23
23
  declare function isPack(value: string): value is Pack;
24
24
 
25
25
  /**
@@ -38,7 +38,7 @@ declare function isPack(value: string): value is Pack;
38
38
  * curl --data "we ran rm -rf /tmp/x" https://… -> DENY dd.rm-rf-absolute
39
39
  *
40
40
  * Without an exemption, a person documenting this tool is blocked by this tool.
41
- * That applies to every command-channel rule — 48 of the 56. The eight `file_glob`
41
+ * That applies to every command-channel rule — 62 of the 74. The twelve `file_glob`
42
42
  * rules never see a command.
43
43
  *
44
44
  * ── Why exemption by CARRIER VERB, and not by "the payload is quoted" ────────
@@ -98,11 +98,18 @@ declare function isPack(value: string): value is Pack;
98
98
  *
99
99
  * ── Honest coverage limits ──────────────────────────────────────────────────
100
100
  *
101
- * - The carrier must be the first word, apart from a single leading `sudo`, which
102
- * is tolerated because it changes privilege rather than semantics — `sudo grep`
103
- * still searches, and no rule triggers on it (`ps.sudo-write` fires on
104
- * `sudo tee|dd|cp|mv|rm|ln|install|chown|chmod|sh`, none of them a carrier).
105
- * A RUNNER prefix is not tolerated: `pnpm exec rg …`, `npx …` and
101
+ * - The carrier need not be the first word: it may follow, and be followed by, up
102
+ * to two QUIET segments — read-only inspection commands (`wc`, `cat`, `head`,
103
+ * `tail`, `ls`, `grep`/family, `rg`) separated by `;`, `&&`, `||` or a pipe — so
104
+ * `wc -l < log; echo "…rm -rf /…"; grep -c x log` is exempt. That is sound
105
+ * because a quiet segment provably neither writes nor executes: `sort` (`-o`,
106
+ * `--compress-program`), `find` (`-exec`), `awk`/`sed`, `tee`/`dd` and `git`
107
+ * (`push`/`reset`) are NOT quiet, so `git commit -m "x" && rm -rf /`,
108
+ * `echo "…" | bash` and `sort … ; echo …` all still deny. A quiet segment carries
109
+ * no quote, so it cannot hide a mention; the mention is in the CARRIER. A single
110
+ * leading `sudo` before the CARRIER is still tolerated (it changes privilege, not
111
+ * meaning; `ps.sudo-write` fires on `sudo tee|dd|cp|mv|rm|ln|install|chown|chmod|sh`,
112
+ * none a carrier). A RUNNER prefix is not tolerated: `pnpm exec rg …`, `npx …` and
106
113
  * `xargs -0 grep …` are not exempt, because "some program eventually execs a
107
114
  * search" is a much weaker claim than "this command is a search".
108
115
  * - At most four quoted arguments are recognised. A fifth is not exempt.
@@ -130,6 +137,27 @@ declare function isPack(value: string): value is Pack;
130
137
  * its own trigger verb.
131
138
  */
132
139
 
140
+ /**
141
+ * The `label` glob for a rule that reads a COMMAND: the two shells plus any MCP tool.
142
+ *
143
+ * A command-channel rule describes a shape — `git push --force`, `rm -rf /` — and
144
+ * that shape reaches the guard on more than one tool. Claude Code's own hooks route
145
+ * `Bash`, `PowerShell` AND `mcp__*` to the guard, and the MCP channel carries the
146
+ * serialized tool input as the same `detail` text every command matcher reads. A
147
+ * rule pinned to `{Bash,PowerShell}` alone silently ignores the MCP channel, so an
148
+ * MCP server that runs shell commands is unguarded. This glob widens the label to
149
+ * the MCP tools as well, and is referenced rather than copied so the set cannot
150
+ * drift rule to rule.
151
+ *
152
+ * `mcp__*` matches the WHOLE label (picomatch matches the entire tool name), so it
153
+ * catches any `mcp__<server>__<tool>` — including Cursor's `mcp__cursor__*` — and
154
+ * nothing else. It does NOT widen the `none_of` exemptions, which stay anchored to
155
+ * the shell: a quoted MENTION is a shell shape, and an MCP tool's serialized JSON
156
+ * does not start with a carrier verb, so a command shape inside an MCP payload is
157
+ * matched, not exempted. Whether that shape is an execution or only a field named
158
+ * like one is a limit the README states, not one this glob can tell apart.
159
+ */
160
+ declare const SHELL_AND_MCP = "{Bash,PowerShell,mcp__*}";
133
161
  /**
134
162
  * A read-only search: `grep`, `rg`, `ag`, `ack`, PowerShell's `Select-String`.
135
163
  *
@@ -175,6 +203,50 @@ declare const HTTP_BODY_MENTION: MatchCondition;
175
203
  * the subset out longhand so the omission is visible at the call site.
176
204
  */
177
205
  declare const QUOTED_MENTION: readonly MatchCondition[];
206
+ /**
207
+ * Zero or more global flags sitting between a command and its subcommand.
208
+ *
209
+ * A command-channel rule that pins a tool to its subcommand CONTIGUOUSLY —
210
+ * `git\s+reset`, `aws\s+s3`, `kubectl\s+delete`, `docker\s+volume` — is defeated
211
+ * by a global flag slipped in between them. The subcommand no longer follows the
212
+ * tool, so the rule stops matching and the action runs unguarded:
213
+ *
214
+ * git -C /repo reset --hard git --no-pager … git -c core.x=y …
215
+ * git --work-tree=/x … aws --profile p … aws --region r …
216
+ * kubectl -n prod delete … docker --context c … docker -H … …
217
+ * terraform -chdir=/x apply … helm -n ns … npm --silent …
218
+ *
219
+ * This constant is the ONE shared fix for that whole class, referenced rather
220
+ * than copied so a rule cannot drift to a weaker spelling. It is spliced in RIGHT
221
+ * AFTER the tool name and BEFORE the `\s+` that precedes the subcommand:
222
+ * `\\bgit${LEADING_FLAGS}\\s+reset\\s+--hard\\b`. Empty (zero flags) it collapses
223
+ * back to the original `tool\s+subcommand`, so the plain form still matches.
224
+ *
225
+ * The forms it tolerates, each as one unit that may repeat:
226
+ * `--no-pager` / `--silent` a bare long flag
227
+ * `-C /repo` / `-n prod` a short flag with a separate value
228
+ * `-c core.pager=cat` a short flag whose value carries `=`
229
+ * `--work-tree=/x` / `-chdir=/x` a flag with an attached `=value`
230
+ * `--profile prod` / `--context c` a long flag with a separate value
231
+ *
232
+ * ── Bounded on purpose ───────────────────────────────────────────────────────
233
+ *
234
+ * Every quantifier is bounded — the run repeats at most a handful of times and
235
+ * each token has a capped length — so the fragment is linear and cannot backtrack
236
+ * catastrophically. A regex whose repetition nests an unbounded quantifier is
237
+ * rejected at parse time; this stays well inside that limit and the length cap.
238
+ *
239
+ * ── What it does NOT cover ───────────────────────────────────────────────────
240
+ *
241
+ * It reads flag SHAPE, not meaning: it cannot tell a flag that consumes the next
242
+ * word as its value from the subcommand itself, so a value token is required not
243
+ * to begin with `-`, which lets a bare flag followed by the subcommand still
244
+ * resolve to the subcommand. It does not cross a shell metacharacter, and it does
245
+ * not read a flag that itself runs another program. An absolute tool path
246
+ * (`/usr/bin/git`) already matches through the `\b` anchor every tool carries;
247
+ * this fragment only closes the gap between the tool and its subcommand.
248
+ */
249
+ declare const LEADING_FLAGS = "(?:\\s+-{1,2}[A-Za-z][\\w-]{0,24}(?:=\\S{1,40})?(?:\\s+[^-\\s]\\S{0,40})?){0,6}";
178
250
 
179
251
  /**
180
252
  * Fixture constructors — three one-line functions, and no zod.
@@ -213,6 +285,20 @@ declare function pwsh(command: string): Fixture;
213
285
  * it only documents the case being proven.
214
286
  */
215
287
  declare function file(filePath: string, tool?: string): Fixture;
288
+ /**
289
+ * A fixture on the command channel, from an MCP tool.
290
+ *
291
+ * The guard hands the engine an MCP call's serialized `tool_input` as the `detail`
292
+ * text (`JSON.stringify(input)`), so a fixture mirrors that: the `input` object is
293
+ * serialized and its JSON becomes the command. `tool` defaults to a generic MCP
294
+ * name; the widened `{Bash,PowerShell,mcp__*}` label matches any `mcp__*`.
295
+ *
296
+ * The point is to prove a command SHAPE carried by an MCP server still fires the
297
+ * rule (`{ command: "rm -rf /" }`), and that a serialized MENTION is NOT exempted:
298
+ * the `none_of` carriers are anchored to the shell, and JSON does not start with a
299
+ * carrier verb.
300
+ */
301
+ declare function mcp(input: Record<string, unknown>, tool?: string): Fixture;
216
302
  /** `git commit -m "…"` — the carrier behind the three hard denies. */
217
303
  declare function mentionInCommit(text: string): Fixture;
218
304
  /** `grep -rn "…" docs/` — searching for the words is not executing them. */
@@ -276,7 +362,7 @@ declare function mentions(text: string): Fixture[];
276
362
  * Pinned to `package.json`'s `version` by `__tests__/stamp.test.ts` — this is a copy
277
363
  * of that value, not an independent one, and the test is what makes the copy safe.
278
364
  */
279
- declare const CATALOG_VERSION = "0.0.2";
365
+ declare const CATALOG_VERSION = "0.2.0";
280
366
  /**
281
367
  * When the rules in this catalog reached their current state, as an ISO-8601
282
368
  * instant. The question it answers is not "when did this reach a registry" but "how
@@ -286,7 +372,7 @@ declare const CATALOG_VERSION = "0.0.2";
286
372
  * the guard handles a missing date, and a wrong date that looks right is worse than
287
373
  * a missing one.
288
374
  */
289
- declare const CATALOG_PUBLISHED_AT = "2026-09-07T12:44:09Z";
375
+ declare const CATALOG_PUBLISHED_AT = "2026-09-18T13:00:33Z";
290
376
 
291
377
  /**
292
378
  * The corpus itself — rules only, and **deliberately free of zod**.
@@ -337,4 +423,4 @@ declare function getRule(id: string): Rule | undefined;
337
423
  /** Every rule in one pack, in authored order. */
338
424
  declare function rulesForPack(pack: Pack): readonly Rule[];
339
425
 
340
- export { CATALOG_PUBLISHED_AT, CATALOG_VERSION, Fixture, GIT_TEXT_MENTION, HTTP_BODY_MENTION, MatchCondition, PACKS, PRINT_MENTION, type Pack, QUOTED_MENTION, RULES, RULES_BY_PACK, Rule, SEARCH_MENTION, bash, file, getRule, isPack, mentionInCommit, mentionInEcho, mentionInPost, mentionInSearch, mentions, pwsh, rulesForPack };
426
+ export { CATALOG_PUBLISHED_AT, CATALOG_VERSION, Fixture, GIT_TEXT_MENTION, HTTP_BODY_MENTION, LEADING_FLAGS, MatchCondition, PACKS, PRINT_MENTION, type Pack, QUOTED_MENTION, RULES, RULES_BY_PACK, Rule, SEARCH_MENTION, SHELL_AND_MCP, bash, file, getRule, isPack, mcp, mentionInCommit, mentionInEcho, mentionInPost, mentionInSearch, mentions, pwsh, rulesForPack };
@@ -3,10 +3,10 @@ export { Action, FixtureInput, Fixtures, Match, RuleInput, Severity } from './sc
3
3
  import 'zod';
4
4
 
5
5
  /**
6
- * The eight packs, and the order they are presented in.
6
+ * The eleven packs, and the order they are presented in.
7
7
  *
8
8
  * These ids are authoritative and shared with the rules authored against them,
9
- * and with the guard's `config.json` `enabledPacks`. Renaming one of these is a
9
+ * and with the guard's `config.json` `disabledPacks`. Renaming one of these is a
10
10
  * breaking change to a user's config file.
11
11
  *
12
12
  * Working-tree destruction leads: destructive git commands are the most frequently
@@ -16,10 +16,10 @@ import 'zod';
16
16
  * Per-pack rule counts are deliberately NOT encoded here: a rule count asserted in
17
17
  * code becomes a reason to write a weak rule to hit a number.
18
18
  */
19
- declare const PACKS: readonly ["working-tree", "destructive-data", "prod-infra", "secret-exposure", "rce-supply-chain", "safety-bypass", "privilege-supply-chain", "file-scope"];
20
- /** One of the eight pack ids. A rule's `category` is always one of these. */
19
+ declare const PACKS: readonly ["working-tree", "destructive-data", "prod-infra", "secret-exposure", "rce-supply-chain", "safety-bypass", "privilege-supply-chain", "file-scope", "agent-context", "test-integrity", "exfiltration"];
20
+ /** One of the eleven pack ids. A rule's `category` is always one of these. */
21
21
  type Pack = (typeof PACKS)[number];
22
- /** Is this string one of the eight pack ids? */
22
+ /** Is this string one of the eleven pack ids? */
23
23
  declare function isPack(value: string): value is Pack;
24
24
 
25
25
  /**
@@ -38,7 +38,7 @@ declare function isPack(value: string): value is Pack;
38
38
  * curl --data "we ran rm -rf /tmp/x" https://… -> DENY dd.rm-rf-absolute
39
39
  *
40
40
  * Without an exemption, a person documenting this tool is blocked by this tool.
41
- * That applies to every command-channel rule — 48 of the 56. The eight `file_glob`
41
+ * That applies to every command-channel rule — 62 of the 74. The twelve `file_glob`
42
42
  * rules never see a command.
43
43
  *
44
44
  * ── Why exemption by CARRIER VERB, and not by "the payload is quoted" ────────
@@ -98,11 +98,18 @@ declare function isPack(value: string): value is Pack;
98
98
  *
99
99
  * ── Honest coverage limits ──────────────────────────────────────────────────
100
100
  *
101
- * - The carrier must be the first word, apart from a single leading `sudo`, which
102
- * is tolerated because it changes privilege rather than semantics — `sudo grep`
103
- * still searches, and no rule triggers on it (`ps.sudo-write` fires on
104
- * `sudo tee|dd|cp|mv|rm|ln|install|chown|chmod|sh`, none of them a carrier).
105
- * A RUNNER prefix is not tolerated: `pnpm exec rg …`, `npx …` and
101
+ * - The carrier need not be the first word: it may follow, and be followed by, up
102
+ * to two QUIET segments — read-only inspection commands (`wc`, `cat`, `head`,
103
+ * `tail`, `ls`, `grep`/family, `rg`) separated by `;`, `&&`, `||` or a pipe — so
104
+ * `wc -l < log; echo "…rm -rf /…"; grep -c x log` is exempt. That is sound
105
+ * because a quiet segment provably neither writes nor executes: `sort` (`-o`,
106
+ * `--compress-program`), `find` (`-exec`), `awk`/`sed`, `tee`/`dd` and `git`
107
+ * (`push`/`reset`) are NOT quiet, so `git commit -m "x" && rm -rf /`,
108
+ * `echo "…" | bash` and `sort … ; echo …` all still deny. A quiet segment carries
109
+ * no quote, so it cannot hide a mention; the mention is in the CARRIER. A single
110
+ * leading `sudo` before the CARRIER is still tolerated (it changes privilege, not
111
+ * meaning; `ps.sudo-write` fires on `sudo tee|dd|cp|mv|rm|ln|install|chown|chmod|sh`,
112
+ * none a carrier). A RUNNER prefix is not tolerated: `pnpm exec rg …`, `npx …` and
106
113
  * `xargs -0 grep …` are not exempt, because "some program eventually execs a
107
114
  * search" is a much weaker claim than "this command is a search".
108
115
  * - At most four quoted arguments are recognised. A fifth is not exempt.
@@ -130,6 +137,27 @@ declare function isPack(value: string): value is Pack;
130
137
  * its own trigger verb.
131
138
  */
132
139
 
140
+ /**
141
+ * The `label` glob for a rule that reads a COMMAND: the two shells plus any MCP tool.
142
+ *
143
+ * A command-channel rule describes a shape — `git push --force`, `rm -rf /` — and
144
+ * that shape reaches the guard on more than one tool. Claude Code's own hooks route
145
+ * `Bash`, `PowerShell` AND `mcp__*` to the guard, and the MCP channel carries the
146
+ * serialized tool input as the same `detail` text every command matcher reads. A
147
+ * rule pinned to `{Bash,PowerShell}` alone silently ignores the MCP channel, so an
148
+ * MCP server that runs shell commands is unguarded. This glob widens the label to
149
+ * the MCP tools as well, and is referenced rather than copied so the set cannot
150
+ * drift rule to rule.
151
+ *
152
+ * `mcp__*` matches the WHOLE label (picomatch matches the entire tool name), so it
153
+ * catches any `mcp__<server>__<tool>` — including Cursor's `mcp__cursor__*` — and
154
+ * nothing else. It does NOT widen the `none_of` exemptions, which stay anchored to
155
+ * the shell: a quoted MENTION is a shell shape, and an MCP tool's serialized JSON
156
+ * does not start with a carrier verb, so a command shape inside an MCP payload is
157
+ * matched, not exempted. Whether that shape is an execution or only a field named
158
+ * like one is a limit the README states, not one this glob can tell apart.
159
+ */
160
+ declare const SHELL_AND_MCP = "{Bash,PowerShell,mcp__*}";
133
161
  /**
134
162
  * A read-only search: `grep`, `rg`, `ag`, `ack`, PowerShell's `Select-String`.
135
163
  *
@@ -175,6 +203,50 @@ declare const HTTP_BODY_MENTION: MatchCondition;
175
203
  * the subset out longhand so the omission is visible at the call site.
176
204
  */
177
205
  declare const QUOTED_MENTION: readonly MatchCondition[];
206
+ /**
207
+ * Zero or more global flags sitting between a command and its subcommand.
208
+ *
209
+ * A command-channel rule that pins a tool to its subcommand CONTIGUOUSLY —
210
+ * `git\s+reset`, `aws\s+s3`, `kubectl\s+delete`, `docker\s+volume` — is defeated
211
+ * by a global flag slipped in between them. The subcommand no longer follows the
212
+ * tool, so the rule stops matching and the action runs unguarded:
213
+ *
214
+ * git -C /repo reset --hard git --no-pager … git -c core.x=y …
215
+ * git --work-tree=/x … aws --profile p … aws --region r …
216
+ * kubectl -n prod delete … docker --context c … docker -H … …
217
+ * terraform -chdir=/x apply … helm -n ns … npm --silent …
218
+ *
219
+ * This constant is the ONE shared fix for that whole class, referenced rather
220
+ * than copied so a rule cannot drift to a weaker spelling. It is spliced in RIGHT
221
+ * AFTER the tool name and BEFORE the `\s+` that precedes the subcommand:
222
+ * `\\bgit${LEADING_FLAGS}\\s+reset\\s+--hard\\b`. Empty (zero flags) it collapses
223
+ * back to the original `tool\s+subcommand`, so the plain form still matches.
224
+ *
225
+ * The forms it tolerates, each as one unit that may repeat:
226
+ * `--no-pager` / `--silent` a bare long flag
227
+ * `-C /repo` / `-n prod` a short flag with a separate value
228
+ * `-c core.pager=cat` a short flag whose value carries `=`
229
+ * `--work-tree=/x` / `-chdir=/x` a flag with an attached `=value`
230
+ * `--profile prod` / `--context c` a long flag with a separate value
231
+ *
232
+ * ── Bounded on purpose ───────────────────────────────────────────────────────
233
+ *
234
+ * Every quantifier is bounded — the run repeats at most a handful of times and
235
+ * each token has a capped length — so the fragment is linear and cannot backtrack
236
+ * catastrophically. A regex whose repetition nests an unbounded quantifier is
237
+ * rejected at parse time; this stays well inside that limit and the length cap.
238
+ *
239
+ * ── What it does NOT cover ───────────────────────────────────────────────────
240
+ *
241
+ * It reads flag SHAPE, not meaning: it cannot tell a flag that consumes the next
242
+ * word as its value from the subcommand itself, so a value token is required not
243
+ * to begin with `-`, which lets a bare flag followed by the subcommand still
244
+ * resolve to the subcommand. It does not cross a shell metacharacter, and it does
245
+ * not read a flag that itself runs another program. An absolute tool path
246
+ * (`/usr/bin/git`) already matches through the `\b` anchor every tool carries;
247
+ * this fragment only closes the gap between the tool and its subcommand.
248
+ */
249
+ declare const LEADING_FLAGS = "(?:\\s+-{1,2}[A-Za-z][\\w-]{0,24}(?:=\\S{1,40})?(?:\\s+[^-\\s]\\S{0,40})?){0,6}";
178
250
 
179
251
  /**
180
252
  * Fixture constructors — three one-line functions, and no zod.
@@ -213,6 +285,20 @@ declare function pwsh(command: string): Fixture;
213
285
  * it only documents the case being proven.
214
286
  */
215
287
  declare function file(filePath: string, tool?: string): Fixture;
288
+ /**
289
+ * A fixture on the command channel, from an MCP tool.
290
+ *
291
+ * The guard hands the engine an MCP call's serialized `tool_input` as the `detail`
292
+ * text (`JSON.stringify(input)`), so a fixture mirrors that: the `input` object is
293
+ * serialized and its JSON becomes the command. `tool` defaults to a generic MCP
294
+ * name; the widened `{Bash,PowerShell,mcp__*}` label matches any `mcp__*`.
295
+ *
296
+ * The point is to prove a command SHAPE carried by an MCP server still fires the
297
+ * rule (`{ command: "rm -rf /" }`), and that a serialized MENTION is NOT exempted:
298
+ * the `none_of` carriers are anchored to the shell, and JSON does not start with a
299
+ * carrier verb.
300
+ */
301
+ declare function mcp(input: Record<string, unknown>, tool?: string): Fixture;
216
302
  /** `git commit -m "…"` — the carrier behind the three hard denies. */
217
303
  declare function mentionInCommit(text: string): Fixture;
218
304
  /** `grep -rn "…" docs/` — searching for the words is not executing them. */
@@ -276,7 +362,7 @@ declare function mentions(text: string): Fixture[];
276
362
  * Pinned to `package.json`'s `version` by `__tests__/stamp.test.ts` — this is a copy
277
363
  * of that value, not an independent one, and the test is what makes the copy safe.
278
364
  */
279
- declare const CATALOG_VERSION = "0.0.2";
365
+ declare const CATALOG_VERSION = "0.2.0";
280
366
  /**
281
367
  * When the rules in this catalog reached their current state, as an ISO-8601
282
368
  * instant. The question it answers is not "when did this reach a registry" but "how
@@ -286,7 +372,7 @@ declare const CATALOG_VERSION = "0.0.2";
286
372
  * the guard handles a missing date, and a wrong date that looks right is worse than
287
373
  * a missing one.
288
374
  */
289
- declare const CATALOG_PUBLISHED_AT = "2026-09-07T12:44:09Z";
375
+ declare const CATALOG_PUBLISHED_AT = "2026-09-18T13:00:33Z";
290
376
 
291
377
  /**
292
378
  * The corpus itself — rules only, and **deliberately free of zod**.
@@ -337,4 +423,4 @@ declare function getRule(id: string): Rule | undefined;
337
423
  /** Every rule in one pack, in authored order. */
338
424
  declare function rulesForPack(pack: Pack): readonly Rule[];
339
425
 
340
- export { CATALOG_PUBLISHED_AT, CATALOG_VERSION, Fixture, GIT_TEXT_MENTION, HTTP_BODY_MENTION, MatchCondition, PACKS, PRINT_MENTION, type Pack, QUOTED_MENTION, RULES, RULES_BY_PACK, Rule, SEARCH_MENTION, bash, file, getRule, isPack, mentionInCommit, mentionInEcho, mentionInPost, mentionInSearch, mentions, pwsh, rulesForPack };
426
+ export { CATALOG_PUBLISHED_AT, CATALOG_VERSION, Fixture, GIT_TEXT_MENTION, HTTP_BODY_MENTION, LEADING_FLAGS, MatchCondition, PACKS, PRINT_MENTION, type Pack, QUOTED_MENTION, RULES, RULES_BY_PACK, Rule, SEARCH_MENTION, SHELL_AND_MCP, bash, file, getRule, isPack, mcp, mentionInCommit, mentionInEcho, mentionInPost, mentionInSearch, mentions, pwsh, rulesForPack };
@@ -3,16 +3,19 @@ import {
3
3
  CATALOG_VERSION,
4
4
  GIT_TEXT_MENTION,
5
5
  HTTP_BODY_MENTION,
6
+ LEADING_FLAGS,
6
7
  PACKS,
7
8
  PRINT_MENTION,
8
9
  QUOTED_MENTION,
9
10
  RULES,
10
11
  RULES_BY_PACK,
11
12
  SEARCH_MENTION,
13
+ SHELL_AND_MCP,
12
14
  bash,
13
15
  file,
14
16
  getRule,
15
17
  isPack,
18
+ mcp,
16
19
  mentionInCommit,
17
20
  mentionInEcho,
18
21
  mentionInPost,
@@ -20,22 +23,25 @@ import {
20
23
  mentions,
21
24
  pwsh,
22
25
  rulesForPack
23
- } from "./chunk-IQBU26ZF.js";
26
+ } from "./chunk-3NDBRXP3.js";
24
27
  export {
25
28
  CATALOG_PUBLISHED_AT,
26
29
  CATALOG_VERSION,
27
30
  GIT_TEXT_MENTION,
28
31
  HTTP_BODY_MENTION,
32
+ LEADING_FLAGS,
29
33
  PACKS,
30
34
  PRINT_MENTION,
31
35
  QUOTED_MENTION,
32
36
  RULES,
33
37
  RULES_BY_PACK,
34
38
  SEARCH_MENTION,
39
+ SHELL_AND_MCP,
35
40
  bash,
36
41
  file,
37
42
  getRule,
38
43
  isPack,
44
+ mcp,
39
45
  mentionInCommit,
40
46
  mentionInEcho,
41
47
  mentionInPost,