@agentvibes/guardrails 0.1.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 (260) hide show
  1. package/README.md +239 -0
  2. package/candidates/README.md +30 -0
  3. package/candidates/r10-as-cast.yml +24 -0
  4. package/candidates/r1d-scattered-numeric-const-in-component.yml +13 -0
  5. package/candidates/r2-helper-fn-in-tsx.yml +22 -0
  6. package/candidates/r3-hooks-outside-layout.yml +21 -0
  7. package/candidates/r6-jsx-comment-smell.yml +14 -0
  8. package/candidates/r7-raw-hsl-outside-tokens.yml +17 -0
  9. package/candidates/r8-inline-style-prop.yml +13 -0
  10. package/candidates/r9-optional-store-arg.yml +24 -0
  11. package/configs/biome.json +96 -0
  12. package/configs/tsconfig.base.json +21 -0
  13. package/dist/astGrep.d.ts +16 -0
  14. package/dist/astGrep.js +48 -0
  15. package/dist/astGrep.js.map +1 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +138 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/deploy.d.ts +1 -0
  20. package/dist/deploy.js +34 -0
  21. package/dist/deploy.js.map +1 -0
  22. package/dist/doctor.d.ts +6 -0
  23. package/dist/doctor.js +102 -0
  24. package/dist/doctor.js.map +1 -0
  25. package/dist/fileWalk.d.ts +2 -0
  26. package/dist/fileWalk.js +45 -0
  27. package/dist/fileWalk.js.map +1 -0
  28. package/dist/findings.d.ts +12 -0
  29. package/dist/findings.js +8 -0
  30. package/dist/findings.js.map +1 -0
  31. package/dist/gateRed.d.ts +1 -0
  32. package/dist/gateRed.js +93 -0
  33. package/dist/gateRed.js.map +1 -0
  34. package/dist/gitDiff.d.ts +18 -0
  35. package/dist/gitDiff.js +90 -0
  36. package/dist/gitDiff.js.map +1 -0
  37. package/dist/hookPostedit.d.ts +1 -0
  38. package/dist/hookPostedit.js +117 -0
  39. package/dist/hookPostedit.js.map +1 -0
  40. package/dist/hookStop.d.ts +1 -0
  41. package/dist/hookStop.js +56 -0
  42. package/dist/hookStop.js.map +1 -0
  43. package/dist/hookStopTest.d.ts +1 -0
  44. package/dist/hookStopTest.js +71 -0
  45. package/dist/hookStopTest.js.map +1 -0
  46. package/dist/init.d.ts +24 -0
  47. package/dist/init.js +172 -0
  48. package/dist/init.js.map +1 -0
  49. package/dist/initTest.d.ts +1 -0
  50. package/dist/initTest.js +88 -0
  51. package/dist/initTest.js.map +1 -0
  52. package/dist/iterate.d.ts +9 -0
  53. package/dist/iterate.js +79 -0
  54. package/dist/iterate.js.map +1 -0
  55. package/dist/iterateTest.d.ts +1 -0
  56. package/dist/iterateTest.js +91 -0
  57. package/dist/iterateTest.js.map +1 -0
  58. package/dist/leakPatterns.d.ts +5 -0
  59. package/dist/leakPatterns.js +22 -0
  60. package/dist/leakPatterns.js.map +1 -0
  61. package/dist/leaks.d.ts +8 -0
  62. package/dist/leaks.js +211 -0
  63. package/dist/leaks.js.map +1 -0
  64. package/dist/leaksRed.d.ts +1 -0
  65. package/dist/leaksRed.js +62 -0
  66. package/dist/leaksRed.js.map +1 -0
  67. package/dist/metrics.d.ts +10 -0
  68. package/dist/metrics.js +113 -0
  69. package/dist/metrics.js.map +1 -0
  70. package/dist/metricsAggregate.d.ts +5 -0
  71. package/dist/metricsAggregate.js +78 -0
  72. package/dist/metricsAggregate.js.map +1 -0
  73. package/dist/metricsBaseline.d.ts +21 -0
  74. package/dist/metricsBaseline.js +88 -0
  75. package/dist/metricsBaseline.js.map +1 -0
  76. package/dist/metricsCollect.d.ts +8 -0
  77. package/dist/metricsCollect.js +295 -0
  78. package/dist/metricsCollect.js.map +1 -0
  79. package/dist/metricsCycle.d.ts +1 -0
  80. package/dist/metricsCycle.js +79 -0
  81. package/dist/metricsCycle.js.map +1 -0
  82. package/dist/metricsTypes.d.ts +102 -0
  83. package/dist/metricsTypes.js +2 -0
  84. package/dist/metricsTypes.js.map +1 -0
  85. package/dist/packagePaths.d.ts +7 -0
  86. package/dist/packagePaths.js +11 -0
  87. package/dist/packagePaths.js.map +1 -0
  88. package/dist/pluginApi.d.ts +24 -0
  89. package/dist/pluginApi.js +12 -0
  90. package/dist/pluginApi.js.map +1 -0
  91. package/dist/pluginResolve.d.ts +6 -0
  92. package/dist/pluginResolve.js +48 -0
  93. package/dist/pluginResolve.js.map +1 -0
  94. package/dist/screenScope.d.ts +21 -0
  95. package/dist/screenScope.js +81 -0
  96. package/dist/screenScope.js.map +1 -0
  97. package/dist/screenScopeTest.d.ts +1 -0
  98. package/dist/screenScopeTest.js +116 -0
  99. package/dist/screenScopeTest.js.map +1 -0
  100. package/dist/severity.d.ts +9 -0
  101. package/dist/severity.js +62 -0
  102. package/dist/severity.js.map +1 -0
  103. package/dist/severityTest.d.ts +1 -0
  104. package/dist/severityTest.js +63 -0
  105. package/dist/severityTest.js.map +1 -0
  106. package/dist/sourceLines.d.ts +9 -0
  107. package/dist/sourceLines.js +52 -0
  108. package/dist/sourceLines.js.map +1 -0
  109. package/dist/structure.d.ts +2 -0
  110. package/dist/structure.js +98 -0
  111. package/dist/structure.js.map +1 -0
  112. package/dist/testRules.d.ts +1 -0
  113. package/dist/testRules.js +285 -0
  114. package/dist/testRules.js.map +1 -0
  115. package/dist/textGrep.d.ts +2 -0
  116. package/dist/textGrep.js +52 -0
  117. package/dist/textGrep.js.map +1 -0
  118. package/dist/tomlTable.d.ts +2 -0
  119. package/dist/tomlTable.js +26 -0
  120. package/dist/tomlTable.js.map +1 -0
  121. package/dist/toolResolve.d.ts +18 -0
  122. package/dist/toolResolve.js +50 -0
  123. package/dist/toolResolve.js.map +1 -0
  124. package/dist/verify.d.ts +3 -0
  125. package/dist/verify.js +49 -0
  126. package/dist/verify.js.map +1 -0
  127. package/dist/verifyDiff.d.ts +18 -0
  128. package/dist/verifyDiff.js +126 -0
  129. package/dist/verifyDiff.js.map +1 -0
  130. package/dist/workspaces.d.ts +6 -0
  131. package/dist/workspaces.js +84 -0
  132. package/dist/workspaces.js.map +1 -0
  133. package/mise.toml +9 -0
  134. package/package.json +62 -0
  135. package/rules/__fixtures__/as-any-escape/bad.ts +8 -0
  136. package/rules/__fixtures__/as-any-escape/good.ts +14 -0
  137. package/rules/__fixtures__/catch-empty/bad.ts +7 -0
  138. package/rules/__fixtures__/catch-empty/good.ts +14 -0
  139. package/rules/__fixtures__/direct-store-import/bad.tsx +23 -0
  140. package/rules/__fixtures__/direct-store-import/good.tsx +34 -0
  141. package/rules/__fixtures__/direct-store-import/showcase/ignored.tsx +9 -0
  142. package/rules/__fixtures__/discriminator-ternary/bad.tsx +10 -0
  143. package/rules/__fixtures__/discriminator-ternary/good.tsx +23 -0
  144. package/rules/__fixtures__/hardcoded-url-in-component/components/bad.tsx +8 -0
  145. package/rules/__fixtures__/hardcoded-url-in-component/components/good.tsx +14 -0
  146. package/rules/__fixtures__/inline-map-row/bad.tsx +72 -0
  147. package/rules/__fixtures__/inline-map-row/good.tsx +51 -0
  148. package/rules/__fixtures__/instanceof-map-set/bad.ts +7 -0
  149. package/rules/__fixtures__/instanceof-map-set/good.ts +8 -0
  150. package/rules/__fixtures__/literal-union-in-component/components/bad.ts +8 -0
  151. package/rules/__fixtures__/literal-union-in-component/components/good.ts +23 -0
  152. package/rules/__fixtures__/missing-observer/bad.tsx +66 -0
  153. package/rules/__fixtures__/missing-observer/good.tsx +69 -0
  154. package/rules/__fixtures__/no-local-kit-clone/bad.ts +30 -0
  155. package/rules/__fixtures__/no-local-kit-clone/bad.tsx +30 -0
  156. package/rules/__fixtures__/no-local-kit-clone/good.ts +31 -0
  157. package/rules/__fixtures__/no-local-kit-clone/good.tsx +31 -0
  158. package/rules/__fixtures__/non-exhaustive-match/bad.ts +8 -0
  159. package/rules/__fixtures__/non-exhaustive-match/good.ts +14 -0
  160. package/rules/__fixtures__/page-store-bare-usememo/bad.ts +15 -0
  161. package/rules/__fixtures__/page-store-bare-usememo/bad.tsx +13 -0
  162. package/rules/__fixtures__/page-store-bare-usememo/good.ts +17 -0
  163. package/rules/__fixtures__/page-store-bare-usememo/good.tsx +14 -0
  164. package/rules/__fixtures__/screen-file-styling/bad.screen.tsx +10 -0
  165. package/rules/__fixtures__/screen-file-styling/good.screen.tsx +8 -0
  166. package/rules/__fixtures__/state-loading-boolean-shape/bad.ts +36 -0
  167. package/rules/__fixtures__/state-loading-boolean-shape/bad.tsx +36 -0
  168. package/rules/__fixtures__/state-loading-boolean-shape/good.ts +44 -0
  169. package/rules/__fixtures__/state-loading-boolean-shape/good.tsx +44 -0
  170. package/rules/__fixtures__/store-async-method/bad.ts +37 -0
  171. package/rules/__fixtures__/store-async-method/bad.tsx +37 -0
  172. package/rules/__fixtures__/store-async-method/good.ts +73 -0
  173. package/rules/__fixtures__/store-async-method/good.tsx +73 -0
  174. package/rules/__fixtures__/store-context-provider/bad.ts +16 -0
  175. package/rules/__fixtures__/store-context-provider/bad.tsx +24 -0
  176. package/rules/__fixtures__/store-context-provider/good.ts +13 -0
  177. package/rules/__fixtures__/store-context-provider/good.tsx +11 -0
  178. package/rules/__fixtures__/store-delegation-getter/bad.ts +30 -0
  179. package/rules/__fixtures__/store-delegation-getter/bad.tsx +30 -0
  180. package/rules/__fixtures__/store-delegation-getter/good.ts +65 -0
  181. package/rules/__fixtures__/store-delegation-getter/good.tsx +65 -0
  182. package/rules/__fixtures__/store-new-map/bad.ts +22 -0
  183. package/rules/__fixtures__/store-new-map/bad.tsx +22 -0
  184. package/rules/__fixtures__/store-new-map/good.ts +41 -0
  185. package/rules/__fixtures__/store-new-map/good.tsx +41 -0
  186. package/rules/__fixtures__/store-no-runinaction/bad.ts +67 -0
  187. package/rules/__fixtures__/store-no-runinaction/bad.tsx +67 -0
  188. package/rules/__fixtures__/store-no-runinaction/good.ts +39 -0
  189. package/rules/__fixtures__/store-no-runinaction/good.tsx +39 -0
  190. package/rules/__fixtures__/store-no-setinterval-poll/bad.ts +19 -0
  191. package/rules/__fixtures__/store-no-setinterval-poll/bad.tsx +19 -0
  192. package/rules/__fixtures__/store-no-setinterval-poll/good.ts +29 -0
  193. package/rules/__fixtures__/store-no-setinterval-poll/good.tsx +29 -0
  194. package/rules/__fixtures__/ts-pattern-dangling-match/bad.ts +20 -0
  195. package/rules/__fixtures__/ts-pattern-dangling-match/good.ts +35 -0
  196. package/rules/__fixtures__/ui-imports-app-store/src/ui/bad.tsx +13 -0
  197. package/rules/__fixtures__/ui-imports-app-store/src/ui/good.tsx +23 -0
  198. package/rules/__fixtures__/view-file-logic/bad.view.tsx +16 -0
  199. package/rules/__fixtures__/view-file-logic/good.view.tsx +7 -0
  200. package/rules/__fixtures__/view-imports-store/bad.view.tsx +13 -0
  201. package/rules/__fixtures__/view-imports-store/good.view.tsx +23 -0
  202. package/rules/__fixtures__/zod-optional-nullable/bad.ts +10 -0
  203. package/rules/__fixtures__/zod-optional-nullable/bad.tsx +17 -0
  204. package/rules/__fixtures__/zod-optional-nullable/good.ts +16 -0
  205. package/rules/__fixtures__/zod-optional-nullable/good.tsx +20 -0
  206. package/rules/as-any-escape-tsx.yml +53 -0
  207. package/rules/as-any-escape.yml +29 -0
  208. package/rules/catch-empty-tsx.yml +55 -0
  209. package/rules/catch-empty.yml +31 -0
  210. package/rules/classname-not-composed.yml +28 -0
  211. package/rules/classname-visual-identity.yml +34 -0
  212. package/rules/demo-mode-by-default-ts.yml +76 -0
  213. package/rules/demo-mode-by-default.yml +54 -0
  214. package/rules/direct-store-import.yml +118 -0
  215. package/rules/discriminator-ternary.yml +70 -0
  216. package/rules/hardcoded-url-in-component.yml +44 -0
  217. package/rules/inline-map-row.yml +107 -0
  218. package/rules/instanceof-map-set-tsx.yml +46 -0
  219. package/rules/instanceof-map-set.yml +22 -0
  220. package/rules/json-roundtrip.yml +9 -0
  221. package/rules/jsx-cond-and.yml +16 -0
  222. package/rules/jsx-ternary.yml +35 -0
  223. package/rules/kind-if-without-match.yml +13 -0
  224. package/rules/literal-union-in-component.yml +71 -0
  225. package/rules/match-bool-to-null.yml +15 -0
  226. package/rules/missing-observer.yml +189 -0
  227. package/rules/mobx-effect-observable-dep.yml +23 -0
  228. package/rules/mobx-effect-store-write.yml +21 -0
  229. package/rules/mobx-usestate-from-store.yml +20 -0
  230. package/rules/no-local-kit-clone-tsx.yml +104 -0
  231. package/rules/no-local-kit-clone.yml +93 -0
  232. package/rules/non-exhaustive-match.yml +34 -0
  233. package/rules/page-store-bare-usememo-tsx.yml +47 -0
  234. package/rules/page-store-bare-usememo.yml +43 -0
  235. package/rules/screen-file-styling.yml +29 -0
  236. package/rules/silent-default-return.yml +17 -0
  237. package/rules/state-loading-boolean-shape-tsx.yml +102 -0
  238. package/rules/state-loading-boolean-shape.yml +91 -0
  239. package/rules/store-async-method-tsx.yml +108 -0
  240. package/rules/store-async-method.yml +98 -0
  241. package/rules/store-context-provider-tsx.yml +68 -0
  242. package/rules/store-context-provider.yml +78 -0
  243. package/rules/store-delegation-getter-tsx.yml +104 -0
  244. package/rules/store-delegation-getter.yml +93 -0
  245. package/rules/store-new-map-tsx.yml +84 -0
  246. package/rules/store-new-map.yml +73 -0
  247. package/rules/store-no-runinaction-tsx.yml +71 -0
  248. package/rules/store-no-runinaction.yml +60 -0
  249. package/rules/store-no-setinterval-poll-tsx.yml +62 -0
  250. package/rules/store-no-setinterval-poll.yml +51 -0
  251. package/rules/ts-pattern-dangling-match-tsx.yml +41 -0
  252. package/rules/ts-pattern-dangling-match.yml +63 -0
  253. package/rules/ui-imports-app-store.yml +49 -0
  254. package/rules/view-file-logic.yml +53 -0
  255. package/rules/view-imports-store.yml +64 -0
  256. package/rules/zod-optional-nullable-tsx.yml +56 -0
  257. package/rules/zod-optional-nullable.yml +29 -0
  258. package/sgconfig.yml +8 -0
  259. package/structure/rules/component-decl.yml +48 -0
  260. package/structure/sgconfig.yml +7 -0
package/README.md ADDED
@@ -0,0 +1,239 @@
1
+ # @agentvibes/guardrails
2
+
3
+ Verification toolkit that makes agents write simple code. One package holds the
4
+ canonical ast-grep rule set (moved here from the agent-skills `defensive-errors`
5
+ skill — this repo is now the source of truth; the skill and hooks consume
6
+ builds of this package), the react structure check, an added-lines diff
7
+ ratchet, and the shared biome/tsconfig presets.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add -D @agentvibes/guardrails
13
+ ```
14
+
15
+ Tools (ast-grep, biome) are not npm dependencies: the CLI resolves them from
16
+ PATH, falls back to `mise x <tool>@<pin>` using the pins in this package's
17
+ `mise.toml`, and prints a one-line install hint when neither works.
18
+
19
+ Installing straight from git (`github:AgentVibes/guardrails#<sha>`) works —
20
+ the `prepare` script builds `dist/` at install time — but pnpm blocks
21
+ lifecycle scripts of git-hosted deps by default, and the unblock knob moved
22
+ between pnpm versions:
23
+
24
+ | pnpm | where to allow the build |
25
+ |---|---|
26
+ | 10.0–10.4 | `package.json` → `pnpm.onlyBuiltDependencies: ["@agentvibes/guardrails"]` |
27
+ | 10.5+ | `pnpm-workspace.yaml` → `onlyBuiltDependencies: ["@agentvibes/guardrails"]` |
28
+ | 11+ | `pnpm-workspace.yaml` → `allowBuilds` keyed by the exact resolved spec |
29
+
30
+ (or install from the registry once the package is published).
31
+
32
+ ## Commands
33
+
34
+ All subcommands accept `--json`.
35
+
36
+ | command | what | exit |
37
+ |---|---|---|
38
+ | `guardrails verify [paths]` | full scan: rule canon + text-greps + structure (one component per file, 120-line error / 90-line warning) | 1 on any error-tier finding |
39
+ | `guardrails verify-diff [--base R]` | ratchet: error-tier findings on lines your diff ADDED vs merge-base; falls back to whole-file, then whole-tree — never to silence | 1 on new findings only |
40
+ | `guardrails doctor` | tool versions + resolution route, ruleset SHA, config discovery | 1 if a tool is missing |
41
+ | `guardrails init` | writes `sgconfig.yml`, `biome.json` (extends the preset), and a detected-and-materialized `[stack]` in `.agentvibes/project.toml` | 0 |
42
+ | `guardrails metrics [paths]` | per-component (loc, hooks, props, observer, JSX depth, branching), per-file (sloc, context-cost) and per-project metrics; `--check` compares the GATED set against the committed `.guardrails/metrics.json` baseline (recomputes facts, never trusts the file); `--update-baseline` tightens it (2% hysteresis, never loosens without `--force`); `--snapshot` appends a JSONL trend row | 1 on ratchet regression, 2 when `--check` finds no baseline |
43
+
44
+ Gated (ratchet, lower = better): p90 component_loc, useState density,
45
+ inline-map-row count, p90 context-cost, runInAction count, async-in-store,
46
+ new-Map-in-store, reactions total, loading-boolean shapes. Everything else is
47
+ observe-only.
48
+
49
+ Counter provenance: runInAction / async-in-store / new-Map-in-store /
50
+ inline-map-row are finding counts of the canon rules (`store-no-runinaction`,
51
+ `store-async-method`, `store-new-map`, `inline-map-row` + tsx twins) — the
52
+ metric counts exactly what `verify` gates. Note the rules' scoping:
53
+ "store" means a class calling `make(Auto)Observable` (not a name/path
54
+ heuristic), and `async_in_store` counts only async methods that mutate `this`
55
+ WITHOUT a `runInAction` patch — the patched ones are already in
56
+ `runInAction_count`, so the two counters partition the should-be-`flow()`
57
+ population without double counting. `reactions_total` is a direct AST
58
+ count (no rule yet). `loading_boolean_shapes` is deliberately NOT the
59
+ `state-loading-boolean-shape` rule: the rule fires only on type-level shapes
60
+ where the outcome is recorded twice (loading flag + second outcome/payload
61
+ field), while the metric also counts lone boolean progress-flag declarations
62
+ (`loading = false` class fields) — the wider Resource<T> migration target.
63
+
64
+ ## CI gate (reusable workflow)
65
+
66
+ Add the whole gate as one job:
67
+
68
+ ```yaml
69
+ jobs:
70
+ guardrails:
71
+ uses: AgentVibes/guardrails/.github/workflows/guardrails-gate.yml@v0
72
+ # with:
73
+ # leaks: true # recommended for public repos
74
+ # base: origin/main # override the merge-base ladder
75
+ ```
76
+
77
+ It checks out with full history, installs the pinned toolchain via mise, runs
78
+ `pnpm install --frozen-lockfile`, then BLOCKING `guardrails verify-diff` and
79
+ `guardrails metrics --check` (skipped with a loud `::notice::` when the repo
80
+ has no `.guardrails/metrics.json` baseline — never silently).
81
+
82
+ Workspaces that reach outside their repo (`link:../<repo>/...` overrides,
83
+ `../../<org>/<repo>` workspace globs) pass
84
+ `sibling_repos: "owner/repo owner2/repo2"`: the gate then checks the main
85
+ repo out at `repos/<org>/<repo>` depth and shallow-clones each sibling at its
86
+ own `repos/<owner>/<name>`, reproducing a `gits/<org>/<repo>` disk layout so
87
+ the relative escapes resolve. Private siblings additionally need
88
+ `secrets: { sibling_token: <PAT with read on them> }` — the default job token
89
+ cannot reach other repos. `@v0` is a
90
+ moving tag that follows validated releases, actions-style; pin `@<commit-sha>`
91
+ if your repo wants immutable supply-chain refs.
92
+
93
+ **Mandatory adoption step — prove the gate can fail.** After wiring the job,
94
+ open a throwaway PR containing an error-tier violation and watch it go red:
95
+
96
+ ```sh
97
+ git checkout -b gate-red-team
98
+ printf 'export const boom = (x: unknown) => x as any;\n' > gateRedTeam.ts
99
+ git add gateRedTeam.ts && git commit -m "red-team the guardrails gate" && git push -u origin gate-red-team
100
+ # open the PR → the guardrails job MUST fail on as-any-escape.
101
+ # Then close the PR and delete the branch.
102
+ ```
103
+
104
+ A gate that has never been seen red proves nothing — do not skip this.
105
+
106
+ ## Stop hook
107
+
108
+ `guardrails hook-stop` is the "cannot say done over red findings" echelon: on
109
+ Claude Code's Stop event it runs the verify-diff ratchet over the session's
110
+ changed files and blocks the stop while NEW error-tier findings remain. Loop
111
+ breaker built in: after 3 blocks in one session (env
112
+ `GUARDRAILS_STOP_MAX_BLOCKS`) it stops blocking and prints a loud warning
113
+ instead — a wedged agent is worse than undercleaned code, and CI holds the
114
+ same line anyway. Like hook-postedit, it never fails the session on its own
115
+ defects.
116
+
117
+ ```json
118
+ { "hooks": { "Stop": [ { "hooks": [{ "type": "command",
119
+ "command": "pnpm exec guardrails hook-stop" }] } ] } }
120
+ ```
121
+
122
+ For a USER-level install (synced settings.json, per-machine tools), route
123
+ both hooks through an opt-hook.sh-style wrapper that no-ops when the tool is
124
+ absent on a host — a bare command errors on every event on machines without
125
+ the package.
126
+
127
+ ## Iterate harness
128
+
129
+ `guardrails iterate --task <file|text> --cmd '<runner>'` is the
130
+ iterate-until-pass loop for cheap-model campaigns: run the agent command, run
131
+ the gate ITSELF (never trusting the agent's report), feed the findings back as
132
+ the next prompt's compact feedback, repeat to green or `--max` (default 6;
133
+ `--gate` overrides the default `verify-diff`). The runner gets the prompt on
134
+ stdin and via `$GUARDRAILS_PROMPT_FILE`, plus `$GUARDRAILS_ATTEMPT`:
135
+
136
+ ```sh
137
+ guardrails iterate --task task.md \
138
+ --cmd 'claude -p "$(cat "$GUARDRAILS_PROMPT_FILE")" --model sonnet' \
139
+ --gate 'pnpm exec guardrails verify-diff && pnpm exec guardrails metrics --check'
140
+ ```
141
+
142
+ Every attempt's tuple (agent exit, gate exit, gate tail, duration) is recorded
143
+ and printed (`--json` for harness pipelines).
144
+
145
+ ## Post-edit hook
146
+
147
+ `guardrails hook-postedit` is the Claude Code PostToolUse hook: it reads the
148
+ hook JSON on stdin, and for an Edit/Write of a .ts/.tsx file scans just that
149
+ file, gating the lines the edit actually changed (merge-base ladder; Write,
150
+ untracked files, and unresolvable bases degrade to whole-file — never to
151
+ silence). Error-tier findings emit a blocking decision, warnings attach as
152
+ context. Wire it in settings.json:
153
+
154
+ ```json
155
+ { "hooks": { "PostToolUse": [ { "matcher": "Edit|Write",
156
+ "hooks": [{ "type": "command", "command": "pnpm exec guardrails hook-postedit" }] } ] } }
157
+ ```
158
+
159
+ ## Leak gate
160
+
161
+ `guardrails leaks [paths]` is the public/private boundary gate: it scans every
162
+ text file for credential patterns, runs gitleaks when available, and exits 1
163
+ on any hit. The package ships only generic credential shapes; house marker
164
+ lists ship via private plugins/config — `.guardrails/leaks.txt`, a
165
+ `[leaks] patterns_file =` manifest entry, or the plugin contract's
166
+ `leakPatterns()` hook (see `@agentvibes/guardrails/plugin`). Pattern files are
167
+ one regex per line (`<id> <regex>`, `#` comments) and are themselves exempt
168
+ from the scan. This repo runs the gate against itself in `pnpm check` and CI.
169
+
170
+ ## Deploy plugins
171
+
172
+ `guardrails deploy [args…]` is an extension point, not a deployer: the public
173
+ CLI resolves a plugin — `plugin = "<npm name>"` under `[deploy]` in
174
+ `.agentvibes/project.toml`, or a single `guardrails-plugin-*` dependency — and
175
+ hands it the args plus the `[deploy]` table. A plugin exports (default or
176
+ named `plugin`) an object `{ name, deploy(args, context) }`; the contract type
177
+ ships as `@agentvibes/guardrails/plugin`. All topology facts (hosts, orgs,
178
+ registries, SSO) live in private plugin packages; `guardrails leaks` enforces
179
+ that this package contains none.
180
+
181
+ ## Presets
182
+
183
+ ```jsonc
184
+ // biome.json
185
+ { "extends": ["@agentvibes/guardrails/biome"] }
186
+ // tsconfig.json
187
+ { "extends": "@agentvibes/guardrails/tsconfig" }
188
+ ```
189
+
190
+ ## Rules and fixtures
191
+
192
+ - `rules/` — the canon. Stable ids, each message is a mini-manual (why + fix).
193
+ Suppress a genuine false positive with
194
+ `// ast-grep-ignore: <rule-id> -- <why>` on the line above.
195
+ - `rules/__fixtures__/` — bad/good fixture pairs; `pnpm test:rules` asserts
196
+ exact hit counts in both directions (a rule that fails to load fails the
197
+ test — silence is not a pass).
198
+ - `structure/` — the `component-decl` marker rule the CLI turns into
199
+ `react-multi-component` / `react-component-too-long` /
200
+ `react-component-needs-folder` findings.
201
+ - `candidates/` — triaged in wave 2. Three PPA R-rules were promoted into
202
+ `rules/`; the eight that remain are repo-local by decision, with the reason
203
+ for each recorded in `candidates/README.md`. Still loaded nowhere.
204
+
205
+ Per-repo severity RAISE: a repo that held a rule stricter than the canon keeps
206
+ its gate via `[severity]` in `.agentvibes/project.toml`
207
+ (`zod-optional-nullable = "error"`) — applied through ast-grep's native
208
+ `--error=<rule-id>` in verify, verify-diff and the post-edit hook, so no
209
+ same-id rule fork is ever needed. Raise-only: downgrades and unknown rule ids
210
+ are refused (exit 2); weakening has its own sanctioned homes (warn-tier biome
211
+ deviation, `[verify] exclude` for vendored trees, justified per-line
212
+ `ast-grep-ignore`).
213
+
214
+ ## Repo-local extra rules
215
+
216
+ A rule that encodes ONE repo's convention does not belong in the canon — but it
217
+ should still run. `guardrails init` already writes the second `ruleDir`
218
+ commented out; uncomment it and drop the rule in:
219
+
220
+ ```yaml
221
+ # sgconfig.yml
222
+ ruleDirs:
223
+ - node_modules/@agentvibes/guardrails/rules
224
+ - .ast-grep/rules # repo-local extras
225
+ ```
226
+
227
+ Both directories load together and findings print their own rule id, so a local
228
+ rule suppresses under its own name. Verified: a canon rule and a repo-local rule
229
+ firing side by side in one `ast-grep scan -c sgconfig.yml` run.
230
+
231
+ Keep local ids distinct from canon ids — ast-grep refuses to load two rules with
232
+ the same id, and a repo-local override of a canon rule is a silent fork rather
233
+ than a fix. If a local rule turns out to be generally useful, promote it here
234
+ with fixtures instead of copying it into a second repo.
235
+
236
+ ## Development
237
+
238
+ `pnpm check` = build (tsc) + biome + fixture harness + gate-can-go-red test.
239
+ CI runs exactly that, blocking.
@@ -0,0 +1,30 @@
1
+ # candidates/ — triaged, wave 2
2
+
3
+ The 11 PPA R-rules that came verbatim from `merkle-substrate/apps/ppa/.ast-grep/rules/`
4
+ have been triaged. **Three were promoted into `rules/` and deleted from here.**
5
+ The eight below stay **repo-local**: they encode a PPA convention rather than a
6
+ house rule, so they keep living in the PPA repo and load through the repo-local
7
+ `ruleDirs` entry (see the package README, "Repo-local extra rules").
8
+
9
+ They are still NOT in the package `sgconfig.yml` and load nowhere from here.
10
+
11
+ ## Promoted to canon
12
+
13
+ | R-rule | canon id | why |
14
+ |---|---|---|
15
+ | r4-ternary-on-discriminator | `discriminator-ternary` | 18 hits outside PPA, and measured **disjoint** from both rules it looked like a duplicate of: `∩ jsx-ternary = 0`, `∩ kind-if-without-match = 0`. The three partition the space — `if` on a discriminator, ternary yielding JSX, ternary yielding values. A `not: is-jsx` clause makes that disjointness structural rather than lucky. |
16
+ | r14-literal-union-in-component | `literal-union-in-component` | Already a house rule — report §8 bans string-literal unions from component files in the `ui/` tier. Globs generalised from `apps/ppa/**` to `**/components/**`, `**/screens/**`, `**/app/**`. |
17
+ | r1a-hardcoded-url-in-component | `hardcoded-url-in-component` | Enforces CLAUDE.md "No localhost": every service URL comes from `dev url`, never a literal. Same reusability boundary `ui-imports-app-store` guards from the other side. |
18
+
19
+ ## Staying repo-local
20
+
21
+ | R-rule | why it is not canon |
22
+ |---|---|
23
+ | r10-as-cast | 121 hits in tg-gallery + observatory alone. `as Type` is legitimate at boundaries this codebase does not yet route through Zod, and `as-any-escape` already covers the escape-hatch half. Canon-promoting it buys a suppression campaign. |
24
+ | r1d-scattered-numeric-const-in-component | 25 hits, but "module-level ALL_CAPS numeric belongs in tokens.ts" is a PPA layout convention, not a house rule. |
25
+ | r2-helper-fn-in-tsx | Its own message admits "unavoidable false positives — it can't distinguish PascalCase observer-wrapped components from helpers". A rule its author documents as broken does not become canon. (The stated cause, "metavariable regex unsupported in ast-grep 0.42", no longer holds on 0.45 — so a corrected version is a real candidate for a later wave, but it would be a new rule, not this one.) |
26
+ | r3-hooks-outside-layout | Bans useState/useEffect/useMemo outside two named layout files. That is PPA's architectural stance; the house rule (C24) permits a hook that owns DOM or lifecycle, and `usePageStore` is sanctioned. |
27
+ | r6-jsx-comment-smell | "A JSX comment is a smell" at severity `hint`. House code uses JSX comments for real explanation (tg-gallery, observatory); this is a PPA style preference. |
28
+ | r7-raw-hsl-outside-tokens | Points at `apps/ppa/lib/tokens.ts`. Tailwind remains a legal manifest value for web apps, so a raw-colour ban cannot be unconditional canon. |
29
+ | r8-inline-style-prop | **Stale as well as local.** It tells you to prefer NativeWind `className` over `style={{}}` — but decision #10 (2026-08-28) converges PPA to *theme-inline*, which makes inline style from a theme hook the intended idiom. The rule now argues against the decision it lives under; PPA should retire or invert it. |
30
+ | r9-optional-store-arg | Flags every `?:` parameter and property in store source. The principle is house-wide (make invalid states unrepresentable), but the rule as written has no way to tell a genuine optional from a modelled one, which is why PPA scoped it to two directories with three ignores. |
@@ -0,0 +1,24 @@
1
+ id: r10-as-cast
2
+ language: typescript
3
+ severity: warning
4
+ message: |
5
+ R10 — `as Type` cast. Use Zod .safeParse() at boundaries, type guards,
6
+ or fix the source type. Allowed: `as const`, `as never` (test stubs),
7
+ `as any` (still flagged separately by defensive-errors as-any-escape).
8
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R10.
9
+ files:
10
+ - "apps/ppa-libs/store/src/**/*.ts"
11
+ - "apps/ppa/**/*.ts"
12
+ - "apps/ppa/**/*.tsx"
13
+ ignores:
14
+ - "**/__tests__/**"
15
+ - "**/*.test.ts"
16
+ - "**/*.test.tsx"
17
+ rule:
18
+ pattern: '$EXPR as $TYPE'
19
+ not:
20
+ any:
21
+ - pattern: '$EXPR as const'
22
+ - pattern: '$EXPR as never'
23
+ - pattern: '$EXPR as any'
24
+ - pattern: '$EXPR as unknown'
@@ -0,0 +1,13 @@
1
+ id: r1d-scattered-numeric-const-in-component
2
+ language: tsx
3
+ severity: warning
4
+ message: |
5
+ R1d — Module-level numeric ALL_CAPS constant in a component file.
6
+ Move to apps/ppa/lib/tokens.ts (layout dimensions) or a per-module
7
+ constants.ts (timeouts, thresholds, limits).
8
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R1.
9
+ files:
10
+ - "apps/ppa/components/**/*.tsx"
11
+ rule:
12
+ kind: variable_declarator
13
+ regex: '^[A-Z][A-Z0-9_]+ = -?[0-9]'
@@ -0,0 +1,22 @@
1
+ id: r2-helper-fn-in-tsx
2
+ language: tsx
3
+ severity: warning
4
+ message: |
5
+ R2 — Helper function declared in a component file. Move to either:
6
+ - the appropriate MobX store as a `get` (computed) or method (action), or
7
+ - apps/ppa/lib/<scope>/ as a stateless utility.
8
+ Component files should be thin views; logic lives in stores.
9
+ Note: this rule has unavoidable false positives — it can't distinguish
10
+ PascalCase observer-wrapped components from helpers without metavariable
11
+ regex (unsupported in ast-grep 0.42). Filter visually or use the
12
+ scripts/lint/helpers-in-tsx.sh wrapper which adds a camelCase guard.
13
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R2.
14
+ files:
15
+ - "apps/ppa/components/**/*.tsx"
16
+ rule:
17
+ any:
18
+ # Arrow functions WITH explicit return type — strongest signal of "helper"
19
+ - kind: variable_declarator
20
+ regex: '^[a-z][a-zA-Z0-9]* = .*\): [A-Za-z<>|& ]+ =>'
21
+ # function declarations
22
+ - pattern: 'function $NAME($$$): $RT { $$$ }'
@@ -0,0 +1,21 @@
1
+ id: r3-hooks-outside-layout
2
+ language: tsx
3
+ severity: warning
4
+ message: |
5
+ R3 — React hook (useState/useCallback/useMemo/useEffect) outside a
6
+ layout/page root. Promote local state to a ui-tier MobX store and
7
+ bind via plain props. The only legitimate hook callsites are:
8
+ - apps/ppa/app/_layout.tsx
9
+ - apps/ppa/app/(app)/_layout.tsx
10
+ - PageStoreManager.getOrCreate(...) wrapping at a route root
11
+ - Animation primitives (mark with `// hooks-ok: animation` comment)
12
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R3.
13
+ files:
14
+ - "apps/ppa/components/**/*.tsx"
15
+ rule:
16
+ any:
17
+ - pattern: 'useState($$$)'
18
+ - pattern: 'useCallback($$$)'
19
+ - pattern: 'useMemo($$$)'
20
+ - pattern: 'useEffect($$$)'
21
+ - pattern: 'useReducer($$$)'
@@ -0,0 +1,14 @@
1
+ id: r6-jsx-comment-smell
2
+ language: tsx
3
+ severity: hint
4
+ message: |
5
+ R6 — JSX comment ({/* ... */}) is a smell. The element it labels
6
+ deserves a name — extract a named observer subcomponent.
7
+ Use the /refactor-jsx-comment slash command to extract.
8
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R6.
9
+ files:
10
+ - "apps/ppa/components/**/*.tsx"
11
+ rule:
12
+ kind: jsx_expression
13
+ has:
14
+ kind: comment
@@ -0,0 +1,17 @@
1
+ id: r7-raw-hsl-outside-tokens
2
+ language: tsx
3
+ severity: warning
4
+ message: |
5
+ R7 — Raw hsl(...) string in a component. Use useTokens() and the
6
+ semantic tokens defined in apps/ppa/lib/tokens.ts. If the token
7
+ you need doesn't exist, ADD it to tokens.ts/themed-tokens.ts first.
8
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R7
9
+ and AGENTS.md (HSL/tokens rule).
10
+ files:
11
+ - "apps/ppa/components/**/*.tsx"
12
+ ignores:
13
+ - "**/_fixture.ts"
14
+ - "**/__tests__/**"
15
+ rule:
16
+ kind: string
17
+ regex: 'hsl\('
@@ -0,0 +1,13 @@
1
+ id: r8-inline-style-prop
2
+ language: tsx
3
+ severity: hint
4
+ message: |
5
+ R8 — Inline style={{}} prop. Prefer NativeWind className for static
6
+ values (color, spacing, layout, radius). Reserve style={{}} for
7
+ genuinely dynamic values: animated transforms, computed widths/positions.
8
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R8.
9
+ files:
10
+ - "apps/ppa/components/**/*.tsx"
11
+ rule:
12
+ kind: jsx_attribute
13
+ regex: '^style=\{\{'
@@ -0,0 +1,24 @@
1
+ id: r9-optional-store-arg
2
+ language: typescript
3
+ severity: warning
4
+ message: |
5
+ R9 — Optional `?:` parameter or property in store API. Either every
6
+ caller passes it (required) or none do (don't define it). For "patch
7
+ any subset" shapes, model as a discriminated union of intents OR
8
+ split into N narrow methods.
9
+ Boundary cases (wrapping isomorphic-git, etc.) are exempt via the
10
+ ignores list below.
11
+ See <vault>/_agent/Notes/2026-05-03-code-quality-rules.md R9.
12
+ files:
13
+ - "apps/ppa-libs/store/src/**/*.ts"
14
+ - "packages/gitapp-store/src/**/*.ts"
15
+ ignores:
16
+ - "**/__tests__/**"
17
+ - "**/contents/iso-git/**"
18
+ - "**/operations.ts"
19
+ rule:
20
+ any:
21
+ - kind: property_signature
22
+ regex: '\?:'
23
+ - kind: required_parameter
24
+ regex: '\?:'
@@ -0,0 +1,96 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.5.10/schema.json",
3
+ "root": false,
4
+ "vcs": {
5
+ "enabled": true,
6
+ "clientKind": "git",
7
+ "useIgnoreFile": true
8
+ },
9
+ "files": {
10
+ "ignoreUnknown": false,
11
+ "includes": [
12
+ "**/*.ts",
13
+ "**/*.tsx",
14
+ "**/*.json",
15
+ "!**/dist",
16
+ "!**/node_modules",
17
+ "!**/*.tsbuildinfo",
18
+ "!pnpm-lock.yaml"
19
+ ]
20
+ },
21
+ "formatter": {
22
+ "enabled": true,
23
+ "indentStyle": "space",
24
+ "indentWidth": 2,
25
+ "lineWidth": 100,
26
+ "lineEnding": "lf"
27
+ },
28
+ "javascript": {
29
+ "formatter": {
30
+ "quoteStyle": "double",
31
+ "trailingCommas": "all",
32
+ "semicolons": "always",
33
+ "arrowParentheses": "always",
34
+ "bracketSpacing": true
35
+ }
36
+ },
37
+ "json": {
38
+ "formatter": {
39
+ "indentWidth": 2,
40
+ "trailingCommas": "none"
41
+ }
42
+ },
43
+ "linter": {
44
+ "enabled": true,
45
+ "rules": {
46
+ "preset": "recommended",
47
+ "complexity": {
48
+ "useArrowFunction": "warn",
49
+ "useLiteralKeys": "error",
50
+ "useOptionalChain": "warn"
51
+ },
52
+ "correctness": {
53
+ "noUnusedImports": "error",
54
+ "noUnusedVariables": "error",
55
+ "useExhaustiveDependencies": "warn"
56
+ },
57
+ "style": {
58
+ "noNonNullAssertion": "off",
59
+ "useAsConstAssertion": "error",
60
+ "useConst": "error",
61
+ "useFilenamingConvention": {
62
+ "level": "error",
63
+ "options": {
64
+ "requireAscii": true,
65
+ "filenameCases": ["camelCase", "PascalCase"]
66
+ }
67
+ },
68
+ "useImportType": "error",
69
+ "useNodejsImportProtocol": "error",
70
+ "useTemplate": "warn",
71
+ "useThrowOnlyError": "error"
72
+ },
73
+ "suspicious": {
74
+ "noConsole": {
75
+ "level": "warn",
76
+ "options": {
77
+ "allow": ["error", "warn"]
78
+ }
79
+ },
80
+ "noEmptyBlockStatements": "error",
81
+ "noExplicitAny": "error"
82
+ },
83
+ "performance": {
84
+ "noBarrelFile": "off"
85
+ }
86
+ }
87
+ },
88
+ "assist": {
89
+ "enabled": true,
90
+ "actions": {
91
+ "source": {
92
+ "organizeImports": "on"
93
+ }
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "bundler",
7
+ "resolveJsonModule": true,
8
+ "strict": true,
9
+ "noUncheckedIndexedAccess": true,
10
+ "exactOptionalPropertyTypes": true,
11
+ "noImplicitOverride": true,
12
+ "noImplicitReturns": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "skipLibCheck": true,
16
+ "esModuleInterop": true,
17
+ "allowSyntheticDefaultImports": true,
18
+ "isolatedModules": true,
19
+ "verbatimModuleSyntax": true
20
+ }
21
+ }
@@ -0,0 +1,16 @@
1
+ import type { Finding, Severity } from "./findings.js";
2
+ export interface AstGrepRow {
3
+ ruleId: string;
4
+ file: string;
5
+ /** 1-based */
6
+ startLine: number;
7
+ /** inclusive line span of the matched node */
8
+ spanLines: number;
9
+ severity: Severity;
10
+ message: string;
11
+ metaText: (name: string) => string | undefined;
12
+ }
13
+ /** Run `ast-grep scan -c <config> --json` and return structured matches. */
14
+ export declare function scan(configPath: string, targets: string[], extraArgs?: string[]): AstGrepRow[];
15
+ /** Reportable (error/warning) rule findings, dropping marker/hint tiers. */
16
+ export declare function scanFindings(configPath: string, targets: string[], extraArgs?: string[]): Finding[];
@@ -0,0 +1,48 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { resolveTool } from "./toolResolve.js";
3
+ /** Run `ast-grep scan -c <config> --json` and return structured matches. */
4
+ export function scan(configPath, targets, extraArgs = []) {
5
+ const tool = resolveTool("ast-grep");
6
+ const [cmd, ...prefix] = tool.argv;
7
+ if (cmd === undefined)
8
+ throw new Error("empty ast-grep argv");
9
+ const res = spawnSync(cmd, [...prefix, "scan", "-c", configPath, ...extraArgs, "--json", ...targets], {
10
+ encoding: "utf8",
11
+ maxBuffer: 256 * 1024 * 1024,
12
+ });
13
+ if (res.error)
14
+ throw res.error;
15
+ const out = res.stdout.trim();
16
+ if (out === "")
17
+ return [];
18
+ let parsed;
19
+ try {
20
+ parsed = JSON.parse(out);
21
+ }
22
+ catch {
23
+ throw new Error(`ast-grep produced unparsable JSON (exit ${res.status}). stderr:\n${res.stderr.slice(0, 2000)}`);
24
+ }
25
+ return parsed.map((m) => ({
26
+ ruleId: m.ruleId,
27
+ file: m.file,
28
+ startLine: m.range.start.line + 1,
29
+ spanLines: m.range.end.line - m.range.start.line + 1,
30
+ severity: m.severity,
31
+ message: m.message,
32
+ metaText: (name) => m.metaVariables?.single?.[name]?.text,
33
+ }));
34
+ }
35
+ /** Reportable (error/warning) rule findings, dropping marker/hint tiers. */
36
+ export function scanFindings(configPath, targets, extraArgs = []) {
37
+ return scan(configPath, targets, extraArgs)
38
+ .filter((r) => r.severity === "error" || r.severity === "warning")
39
+ .map((r) => ({
40
+ rule: r.ruleId,
41
+ severity: r.severity,
42
+ file: r.file,
43
+ line: r.startLine,
44
+ message: r.message,
45
+ source: "ast-grep",
46
+ }));
47
+ }
48
+ //# sourceMappingURL=astGrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astGrep.js","sourceRoot":"","sources":["../src/astGrep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAuB/C,4EAA4E;AAC5E,MAAM,UAAU,IAAI,CAClB,UAAkB,EAClB,OAAiB,EACjB,YAAsB,EAAE;IAExB,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;IACnC,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC9D,MAAM,GAAG,GAAG,SAAS,CACnB,GAAG,EACH,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,EACzE;QACE,QAAQ,EAAE,MAAM;QAChB,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI;KAC7B,CACF,CAAC;IACF,IAAI,GAAG,CAAC,KAAK;QAAE,MAAM,GAAG,CAAC,KAAK,CAAC;IAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,MAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,2CAA2C,GAAG,CAAC,MAAM,eAAe,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAChG,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;QACjC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC;QACpD,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI;KAClE,CAAC,CAAC,CAAC;AACN,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,YAAY,CAC1B,UAAkB,EAClB,OAAiB,EACjB,YAAsB,EAAE;IAExB,OAAO,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC;SACxC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC;SACjE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,CAAC,CAAC,MAAM;QACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,SAAS;QACjB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,UAAmB;KAC5B,CAAC,CAAC,CAAC;AACR,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};