@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/dist/cli.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { match } from "ts-pattern";
4
+ import { runDeploy } from "./deploy.js";
5
+ import { runDoctor } from "./doctor.js";
6
+ import { runHookPostedit } from "./hookPostedit.js";
7
+ import { runHookStop } from "./hookStop.js";
8
+ import { runInit } from "./init.js";
9
+ import { runIterate } from "./iterate.js";
10
+ import { runLeaks } from "./leaks.js";
11
+ import { runMetrics } from "./metrics.js";
12
+ import { ToolMissingError } from "./toolResolve.js";
13
+ import { runVerify } from "./verify.js";
14
+ import { runVerifyDiff } from "./verifyDiff.js";
15
+ const USAGE = `guardrails — verification toolkit that makes agents write simple code
16
+
17
+ Usage:
18
+ guardrails verify [paths...] scan with the bundled rule canon (+ text
19
+ greps + react structure); exit 1 on any
20
+ error-tier finding
21
+ guardrails verify-diff [--base R] added-lines ratchet vs merge-base; exit 1
22
+ only on error-tier findings your diff added
23
+ guardrails doctor tool versions, ruleset SHA, config discovery
24
+ guardrails init write repo stubs: sgconfig.yml, biome.json,
25
+ [stack] in .agentvibes/project.toml
26
+ guardrails metrics [paths...] per-component/file/project metrics; with
27
+ --check compares GATED metrics against the
28
+ committed baseline (.guardrails/metrics.json)
29
+ and exits 1 on any regression;
30
+ --update-baseline [--force] tightens the
31
+ baseline (2% hysteresis); --snapshot appends
32
+ a JSONL trend row; --baseline <path> overrides
33
+
34
+ guardrails leaks [paths] public/private boundary gate: scan for
35
+ private-infrastructure markers and token
36
+ patterns (+ gitleaks when available);
37
+ exit 1 on any hit
38
+ guardrails deploy [args...] hand off to the project's deploy plugin
39
+ (guardrails-plugin-* dep, or [deploy]
40
+ plugin= in .agentvibes/project.toml);
41
+ the public CLI carries no topology
42
+ guardrails hook-postedit Claude Code PostToolUse hook: reads hook
43
+ JSON on stdin, scans the edited .ts/.tsx
44
+ file on its changed lines; blocks on
45
+ error-tier findings
46
+ guardrails hook-stop Claude Code Stop hook: verify-diff over
47
+ the session's changed files; blocks
48
+ "done" over new error-tier findings
49
+ (max 3 blocks/session, then loud skip)
50
+ guardrails iterate --task T --cmd C iterate-until-pass harness: run agent
51
+ command, run the gate, feed findings
52
+ back, repeat to green or --max (6)
53
+
54
+ All subcommands accept --json.
55
+ Exit codes: 0 ok · 1 findings/regression · 2 usage, missing tool, or missing baseline
56
+ `;
57
+ async function main(argv) {
58
+ const args = [...argv];
59
+ const json = args.includes("--json");
60
+ const filtered = args.filter((a) => a !== "--json");
61
+ const command = filtered.shift();
62
+ if (command === undefined || command === "--help" || command === "-h" || command === "help") {
63
+ console.log(USAGE);
64
+ return command === undefined ? 2 : 0;
65
+ }
66
+ const takeValueFlag = (flag) => {
67
+ const idx = filtered.indexOf(flag);
68
+ if (idx < 0)
69
+ return undefined;
70
+ const value = filtered[idx + 1];
71
+ // ast-grep-ignore: silent-default-return -- deliberate tri-state, not a fallback: null means "flag present but value missing" and every caller turns it into a usage error
72
+ if (value === undefined)
73
+ return null;
74
+ filtered.splice(idx, 2);
75
+ return value;
76
+ };
77
+ const takeBoolFlag = (flag) => {
78
+ const idx = filtered.indexOf(flag);
79
+ if (idx < 0)
80
+ return false;
81
+ filtered.splice(idx, 1);
82
+ return true;
83
+ };
84
+ const base = takeValueFlag("--base");
85
+ if (base === null) {
86
+ console.error("guardrails: --base requires a ref argument");
87
+ return 2;
88
+ }
89
+ const baselinePath = takeValueFlag("--baseline");
90
+ if (baselinePath === null) {
91
+ console.error("guardrails: --baseline requires a path argument");
92
+ return 2;
93
+ }
94
+ const patternsFile = takeValueFlag("--patterns") ?? undefined;
95
+ const task = takeValueFlag("--task") ?? undefined;
96
+ const cmd = takeValueFlag("--cmd") ?? undefined;
97
+ const gateCmd = takeValueFlag("--gate") ?? undefined;
98
+ const max = takeValueFlag("--max") ?? undefined;
99
+ const check = takeBoolFlag("--check");
100
+ const updateBaseline = takeBoolFlag("--update-baseline");
101
+ const force = takeBoolFlag("--force");
102
+ const snapshot = takeBoolFlag("--snapshot");
103
+ // ast-grep-ignore: non-exhaustive-match -- `command` is arbitrary CLI input (open string), not a closed union; the otherwise arm IS the unknown-subcommand error path
104
+ return match(command)
105
+ .with("verify", () => runVerify(filtered, json))
106
+ .with("verify-diff", () => runVerifyDiff(base, json))
107
+ .with("doctor", () => runDoctor(json))
108
+ .with("init", () => runInit(json))
109
+ .with("metrics", () => runMetrics({ targets: filtered, json, check, updateBaseline, force, snapshot, baselinePath }))
110
+ .with("leaks", () => runLeaks(filtered, json, patternsFile))
111
+ .with("deploy", () => runDeploy(filtered, json))
112
+ .with("hook-postedit", () => runHookPostedit())
113
+ .with("hook-stop", () => runHookStop())
114
+ .with("iterate", () => runIterate({
115
+ task,
116
+ cmd,
117
+ gate: gateCmd,
118
+ max: Number(max ?? 6),
119
+ json,
120
+ cliPath: fileURLToPath(import.meta.url),
121
+ }))
122
+ .otherwise(() => {
123
+ console.error(`guardrails: unknown subcommand '${command}'\n`);
124
+ console.error(USAGE);
125
+ return 2;
126
+ });
127
+ }
128
+ try {
129
+ process.exit(await main(process.argv.slice(2)));
130
+ }
131
+ catch (err) {
132
+ if (err instanceof ToolMissingError) {
133
+ console.error(`guardrails: ${err.message}`);
134
+ process.exit(2);
135
+ }
136
+ throw err;
137
+ }
138
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runDeploy(args: string[], json: boolean): Promise<number>;
package/dist/deploy.js ADDED
@@ -0,0 +1,34 @@
1
+ import { join } from "node:path";
2
+ import { loadPlugin, PluginResolutionError, resolvePluginName } from "./pluginResolve.js";
3
+ import { readTomlTable } from "./tomlTable.js";
4
+ const DOC = "See README 'Deploy plugins': install a guardrails-plugin-* package (or set `plugin = \"<npm name>\"` under [deploy] in .agentvibes/project.toml). The public CLI carries no deploy topology of its own.";
5
+ export async function runDeploy(args, json) {
6
+ const cwd = process.cwd();
7
+ const deployConfig = readTomlTable(join(cwd, ".agentvibes", "project.toml"), "deploy");
8
+ let pluginName;
9
+ try {
10
+ pluginName = resolvePluginName(cwd);
11
+ }
12
+ catch (err) {
13
+ console.error(`guardrails deploy: ${err instanceof Error ? err.message : String(err)}`);
14
+ return 2;
15
+ }
16
+ if (pluginName === undefined) {
17
+ console.error(`guardrails deploy: no deploy plugin found in this project. ${DOC}`);
18
+ return 2;
19
+ }
20
+ let plugin;
21
+ try {
22
+ plugin = await loadPlugin(cwd, pluginName);
23
+ }
24
+ catch (err) {
25
+ if (err instanceof PluginResolutionError) {
26
+ console.error(`guardrails deploy: ${err.message} ${DOC}`);
27
+ return 2;
28
+ }
29
+ throw err;
30
+ }
31
+ const context = { cwd, deployConfig, json };
32
+ return await plugin.deploy(args, context);
33
+ }
34
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../src/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,GAAG,GACP,yMAAyM,CAAC;AAE5M,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,IAAa;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEvF,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,8DAA8D,GAAG,EAAE,CAAC,CAAC;QACnF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,MAA8C,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,sBAAsB,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAkB,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;IAC3D,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Stable digest of every bundled rule file (canon + structure marker), so two
3
+ * hosts can compare "which ruleset did your gate actually run" by one string.
4
+ */
5
+ export declare function rulesetSha(): string;
6
+ export declare function runDoctor(json: boolean): number;
package/dist/doctor.js ADDED
@@ -0,0 +1,102 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { packageRoot, rulesDir, structureRulesDir } from "./packagePaths.js";
5
+ import { pinnedVersion, tryResolveTool } from "./toolResolve.js";
6
+ function reportTool(name) {
7
+ const pin = pinnedVersion(name);
8
+ const resolved = tryResolveTool(name);
9
+ if (resolved === undefined) {
10
+ return {
11
+ found: false,
12
+ ...(pin !== undefined ? { pin } : {}),
13
+ hint: `mise use -g ${name}@${pin ?? "latest"}`,
14
+ };
15
+ }
16
+ return {
17
+ found: true,
18
+ version: resolved.version,
19
+ via: resolved.via,
20
+ ...(pin !== undefined ? { pin } : {}),
21
+ };
22
+ }
23
+ /**
24
+ * Stable digest of every bundled rule file (canon + structure marker), so two
25
+ * hosts can compare "which ruleset did your gate actually run" by one string.
26
+ */
27
+ export function rulesetSha() {
28
+ const hash = createHash("sha256");
29
+ for (const dir of [rulesDir, structureRulesDir]) {
30
+ const files = readdirSync(dir)
31
+ .filter((f) => f.endsWith(".yml"))
32
+ .sort();
33
+ for (const f of files) {
34
+ hash.update(f);
35
+ hash.update("\0");
36
+ hash.update(readFileSync(join(dir, f)));
37
+ hash.update("\0");
38
+ }
39
+ }
40
+ return hash.digest("hex");
41
+ }
42
+ function discoverConfigs(cwd) {
43
+ const found = {};
44
+ const sg = join(cwd, "sgconfig.yml");
45
+ found["sgconfig.yml"] = existsSync(sg) ? "present" : "absent";
46
+ const biomePath = join(cwd, "biome.json");
47
+ if (existsSync(biomePath)) {
48
+ const extendsPreset = readFileSync(biomePath, "utf8").includes("@agentvibes/guardrails/biome");
49
+ found["biome.json"] = extendsPreset
50
+ ? "present, extends @agentvibes/guardrails/biome"
51
+ : "present";
52
+ }
53
+ else {
54
+ found["biome.json"] = "absent";
55
+ }
56
+ const tomlPath = join(cwd, ".agentvibes", "project.toml");
57
+ if (existsSync(tomlPath)) {
58
+ const hasStack = /^\[stack\]/m.test(readFileSync(tomlPath, "utf8"));
59
+ found[".agentvibes/project.toml"] = hasStack ? "present, has [stack]" : "present, no [stack]";
60
+ }
61
+ else {
62
+ found[".agentvibes/project.toml"] = "absent";
63
+ }
64
+ return found;
65
+ }
66
+ export function runDoctor(json) {
67
+ const version = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")).version;
68
+ const result = {
69
+ command: "doctor",
70
+ package: { name: "@agentvibes/guardrails", version, root: packageRoot },
71
+ node: process.version,
72
+ tools: {
73
+ "ast-grep": reportTool("ast-grep"),
74
+ biome: reportTool("biome"),
75
+ mise: tryResolveTool("mise") !== undefined,
76
+ },
77
+ rulesetSha: rulesetSha(),
78
+ configDiscovery: discoverConfigs(process.cwd()),
79
+ };
80
+ if (json) {
81
+ console.log(JSON.stringify(result, null, 2));
82
+ }
83
+ else {
84
+ console.log(`@agentvibes/guardrails ${version} (${packageRoot})`);
85
+ console.log(`node ${result.node}`);
86
+ for (const name of ["ast-grep", "biome"]) {
87
+ const t = result.tools[name];
88
+ console.log(t.found
89
+ ? `${name.padEnd(10)} ${t.version} (via ${t.via}${t.pin !== undefined ? `, pin ${t.pin}` : ""})`
90
+ : `${name.padEnd(10)} MISSING — install: ${t.hint}`);
91
+ }
92
+ console.log(`mise ${result.tools.mise ? "present" : "absent"}`);
93
+ console.log(`ruleset sha256:${result.rulesetSha.slice(0, 16)}…`);
94
+ console.log(`config discovery in ${process.cwd()}:`);
95
+ for (const [k, v] of Object.entries(result.configDiscovery)) {
96
+ console.log(` ${k.padEnd(28)} ${v}`);
97
+ }
98
+ }
99
+ const allFound = result.tools["ast-grep"].found && result.tools.biome.found;
100
+ return allFound ? 0 : 1;
101
+ }
102
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUjE,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,IAAI,EAAE,eAAe,IAAI,IAAI,GAAG,IAAI,QAAQ,EAAE;SAC/C,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC;aAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aACjC,IAAI,EAAE,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACrC,KAAK,CAAC,cAAc,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE9D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC1C,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,aAAa,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC,CAAC;QAC/F,KAAK,CAAC,YAAY,CAAC,GAAG,aAAa;YACjC,CAAC,CAAC,+CAA+C;YACjD,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC;IACjC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;IAC1D,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QACpE,KAAK,CAAC,0BAA0B,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAChG,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,0BAA0B,CAAC,GAAG,QAAQ,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CACnE,CAAC,OAAO,CAAC;IACV,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,QAAQ;QACjB,OAAO,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE;QACvE,IAAI,EAAE,OAAO,CAAC,OAAO;QACrB,KAAK,EAAE;YACL,UAAU,EAAE,UAAU,CAAC,UAAU,CAAC;YAClC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;YAC1B,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,SAAS;SAC3C;QACD,UAAU,EAAE,UAAU,EAAE;QACxB,eAAe,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;KAChD,CAAC;IAEF,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,0BAA0B,OAAO,KAAK,WAAW,GAAG,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,OAAO,CAAU,EAAE,CAAC;YAClD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAG,CACT,CAAC,CAAC,KAAK;gBACL,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;gBAChG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,IAAI,EAAE,CACtD,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;IAC5E,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** Expand file/dir targets into the contained files matching `exts`. Does not follow symlinks. */
2
+ export declare function collectFiles(targets: string[], exts: string[]): string[];
@@ -0,0 +1,45 @@
1
+ import { lstatSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ // `.claude` is agent workspace, not product source — and in the wild it holds
4
+ // symlinked git worktrees of OTHER repos (observed in observatory), which is
5
+ // also why the walker lstats and never follows symlinks: a symlinked foreign
6
+ // tree must not leak into this project's metrics or scans.
7
+ const SKIP_DIRS = new Set([
8
+ "node_modules",
9
+ "dist",
10
+ "build",
11
+ ".next",
12
+ ".git",
13
+ "coverage",
14
+ ".turbo",
15
+ ".claude",
16
+ ]);
17
+ /** Expand file/dir targets into the contained files matching `exts`. Does not follow symlinks. */
18
+ export function collectFiles(targets, exts) {
19
+ const out = [];
20
+ const visit = (path) => {
21
+ let st;
22
+ try {
23
+ st = lstatSync(path);
24
+ }
25
+ catch {
26
+ return;
27
+ }
28
+ if (st.isFile()) {
29
+ if (exts.some((e) => path.endsWith(e)))
30
+ out.push(path);
31
+ return;
32
+ }
33
+ if (!st.isDirectory())
34
+ return;
35
+ for (const entry of readdirSync(path)) {
36
+ if (SKIP_DIRS.has(entry))
37
+ continue;
38
+ visit(join(path, entry));
39
+ }
40
+ };
41
+ for (const t of targets)
42
+ visit(t);
43
+ return out;
44
+ }
45
+ //# sourceMappingURL=fileWalk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileWalk.js","sourceRoot":"","sources":["../src/fileWalk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,2DAA2D;AAC3D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,cAAc;IACd,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,UAAU;IACV,QAAQ;IACR,SAAS;CACV,CAAC,CAAC;AAEH,kGAAkG;AAClG,MAAM,UAAU,YAAY,CAAC,OAAiB,EAAE,IAAc;IAC5D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE;QACnC,IAAI,EAAgC,CAAC;QACrC,IAAI,CAAC;YACH,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;YAChB,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvD,OAAO;QACT,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE;YAAE,OAAO;QAC9B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YACnC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type Severity = "error" | "warning" | "info" | "hint";
2
+ export type FindingSource = "ast-grep" | "text-grep" | "structure";
3
+ export interface Finding {
4
+ rule: string;
5
+ severity: Severity;
6
+ file: string;
7
+ line: number;
8
+ message: string;
9
+ source: FindingSource;
10
+ }
11
+ export declare function formatFinding(f: Finding): string;
12
+ export declare function hasErrors(findings: Finding[]): boolean;
@@ -0,0 +1,8 @@
1
+ export function formatFinding(f) {
2
+ const firstLine = f.message.split("\n")[0] ?? "";
3
+ return `${f.severity}[${f.rule}]: ${f.file}:${f.line}\n ${firstLine}`;
4
+ }
5
+ export function hasErrors(findings) {
6
+ return findings.some((f) => f.severity === "error");
7
+ }
8
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,OAAO,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,OAAO,SAAS,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAmB;IAC3C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { packageRoot } from "./packagePaths.js";
6
+ // The gate-can-go-red test. A CI step that only ever sees green input proves
7
+ // nothing — `|| echo warning` gates and no-op lint scripts stay green forever,
8
+ // and that anti-pattern is what this package exists to kill. So `pnpm check`
9
+ // runs the verifier against a deliberately bad file and FAILS unless the
10
+ // verifier fails, then against a clean file and fails unless it passes.
11
+ const BAD_SOURCE = `interface Payload { id: string }
12
+ export function decode(raw: unknown): Payload {
13
+ return raw as any;
14
+ }
15
+ `;
16
+ const GOOD_SOURCE = `export function double(n: number): number {
17
+ return n * 2;
18
+ }
19
+ `;
20
+ function verify(target) {
21
+ const res = spawnSync("node", [join(packageRoot, "dist", "cli.js"), "verify", target], {
22
+ encoding: "utf8",
23
+ });
24
+ if (res.error)
25
+ throw res.error;
26
+ return res.status ?? -1;
27
+ }
28
+ // The `[verify] exclude` vendored-tree carve-out, both directions: a bad file
29
+ // INSIDE the excluded tree must not gate verify-diff, and the same bad file
30
+ // outside it must — otherwise the carve-out would be indistinguishable from a
31
+ // broken gate.
32
+ function excludeCarveOut(base) {
33
+ const repo = join(base, "excludeRepo");
34
+ mkdirSync(join(repo, "vendored"), { recursive: true });
35
+ mkdirSync(join(repo, ".agentvibes"), { recursive: true });
36
+ const git = (...args) => {
37
+ const r = spawnSync("git", ["-C", repo, ...args], { encoding: "utf8" });
38
+ if (r.status !== 0)
39
+ throw new Error(`git ${args.join(" ")}: ${r.stderr}`);
40
+ };
41
+ git("init", "-q");
42
+ writeFileSync(join(repo, ".agentvibes", "project.toml"), '[verify]\nexclude = "^vendored/"\n');
43
+ writeFileSync(join(repo, "clean.ts"), GOOD_SOURCE);
44
+ git("add", "-A");
45
+ git("-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "init");
46
+ const run = () => {
47
+ const res = spawnSync("node", [join(packageRoot, "dist", "cli.js"), "verify-diff"], {
48
+ cwd: repo,
49
+ encoding: "utf8",
50
+ });
51
+ return res.status ?? -1;
52
+ };
53
+ writeFileSync(join(repo, "vendored", "upstreamCopy.ts"), BAD_SOURCE);
54
+ if (run() !== 0) {
55
+ console.error("FAIL gate-red: excluded vendored file gated verify-diff — carve-out inert");
56
+ return 1;
57
+ }
58
+ console.log(" ok gate-red: bad file under [verify] exclude → verify-diff exit 0 (carve-out)");
59
+ writeFileSync(join(repo, "notVendored.ts"), BAD_SOURCE);
60
+ if (run() !== 1) {
61
+ console.error("FAIL gate-red: bad file OUTSIDE the exclude did not gate — exclude too broad");
62
+ return 1;
63
+ }
64
+ console.log(" ok gate-red: same bad file outside the exclude → verify-diff exit 1");
65
+ return 0;
66
+ }
67
+ function main() {
68
+ const dir = mkdtempSync(join(tmpdir(), "guardrails-gate-red-"));
69
+ try {
70
+ const badFile = join(dir, "deliberatelyBad.ts");
71
+ writeFileSync(badFile, BAD_SOURCE);
72
+ const badExit = verify(badFile);
73
+ if (badExit !== 1) {
74
+ console.error(`FAIL gate-red: verify on a file containing \`as any\` exited ${badExit}, expected 1 — the gate cannot go red`);
75
+ return 1;
76
+ }
77
+ console.log(" ok gate-red: deliberately bad file → verify exit 1 (gate goes red)");
78
+ const goodFile = join(dir, "deliberatelyGood.ts");
79
+ writeFileSync(goodFile, GOOD_SOURCE);
80
+ const goodExit = verify(goodFile);
81
+ if (goodExit !== 0) {
82
+ console.error(`FAIL gate-red: verify on a clean file exited ${goodExit}, expected 0`);
83
+ return 1;
84
+ }
85
+ console.log(" ok gate-red: clean file → verify exit 0 (gate goes green)");
86
+ return excludeCarveOut(dir);
87
+ }
88
+ finally {
89
+ rmSync(dir, { recursive: true, force: true });
90
+ }
91
+ }
92
+ process.exit(main());
93
+ //# sourceMappingURL=gateRed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateRed.js","sourceRoot":"","sources":["../src/gateRed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare function gitRoot(cwd: string): string | undefined;
2
+ /**
3
+ * The base-ref ladder, ported from defensive-errors hook-postedit.sh.
4
+ * `origin/HEAD` first because it names the remote's real default branch. A
5
+ * LOCAL branch whose merge-base is HEAD itself is rejected: on a repo with no
6
+ * remote, checked out on `main`, the ref `main` IS HEAD — the diff would
7
+ * degenerate to `git diff HEAD` and committing mid-session would launder the
8
+ * findings. A remote ref in the same position is fine: it does not move on a
9
+ * local commit, so the degenerate case self-heals on the next commit.
10
+ */
11
+ export declare function resolveMergeBase(root: string, explicitBase?: string): string | undefined;
12
+ /** Changed (ACMR) tracked .ts/.tsx files vs the merge base, plus untracked ones. Absolute paths. */
13
+ export declare function changedFiles(root: string, mergeBase: string): {
14
+ tracked: string[];
15
+ untracked: string[];
16
+ };
17
+ /** 1-based line numbers ADDED in the working tree vs mergeBase for one file. */
18
+ export declare function addedLines(root: string, mergeBase: string, file: string): Set<number>;
@@ -0,0 +1,90 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { isAbsolute, join } from "node:path";
3
+ function git(root, args) {
4
+ const res = spawnSync("git", root === undefined ? args : ["-C", root, ...args], {
5
+ encoding: "utf8",
6
+ maxBuffer: 256 * 1024 * 1024,
7
+ });
8
+ if (res.status !== 0 || res.error)
9
+ return undefined;
10
+ return res.stdout;
11
+ }
12
+ export function gitRoot(cwd) {
13
+ return git(cwd, ["rev-parse", "--show-toplevel"])?.trim();
14
+ }
15
+ /**
16
+ * The base-ref ladder, ported from defensive-errors hook-postedit.sh.
17
+ * `origin/HEAD` first because it names the remote's real default branch. A
18
+ * LOCAL branch whose merge-base is HEAD itself is rejected: on a repo with no
19
+ * remote, checked out on `main`, the ref `main` IS HEAD — the diff would
20
+ * degenerate to `git diff HEAD` and committing mid-session would launder the
21
+ * findings. A remote ref in the same position is fine: it does not move on a
22
+ * local commit, so the degenerate case self-heals on the next commit.
23
+ */
24
+ export function resolveMergeBase(root, explicitBase) {
25
+ const headOid = git(root, ["rev-parse", "HEAD"])?.trim();
26
+ if (headOid === undefined)
27
+ return undefined;
28
+ const originHead = git(root, [
29
+ "symbolic-ref",
30
+ "-q",
31
+ "--short",
32
+ "refs/remotes/origin/HEAD",
33
+ ])?.trim();
34
+ const candidates = explicitBase !== undefined
35
+ ? [explicitBase]
36
+ : [
37
+ originHead,
38
+ "origin/main",
39
+ "origin/master",
40
+ "upstream/main",
41
+ "upstream/master",
42
+ "main",
43
+ "master",
44
+ ];
45
+ for (const ref of candidates) {
46
+ if (ref === undefined || ref === "")
47
+ continue;
48
+ if (git(root, ["rev-parse", "-q", "--verify", `${ref}^{commit}`]) === undefined)
49
+ continue;
50
+ const mb = git(root, ["merge-base", "HEAD", ref])?.trim();
51
+ if (mb === undefined || mb === "")
52
+ continue;
53
+ const isRemote = ref.includes("/");
54
+ if (!isRemote && mb === headOid && explicitBase === undefined)
55
+ continue;
56
+ return mb;
57
+ }
58
+ return undefined;
59
+ }
60
+ /** Changed (ACMR) tracked .ts/.tsx files vs the merge base, plus untracked ones. Absolute paths. */
61
+ export function changedFiles(root, mergeBase) {
62
+ const abs = (f) => (isAbsolute(f) ? f : join(root, f));
63
+ const tracked = (git(root, ["diff", "--name-only", "--diff-filter=ACMR", mergeBase, "--", "*.ts", "*.tsx"]) ?? "")
64
+ .split("\n")
65
+ .filter((f) => f !== "")
66
+ .map(abs);
67
+ const untracked = (git(root, ["ls-files", "--others", "--exclude-standard", "--", "*.ts", "*.tsx"]) ?? "")
68
+ .split("\n")
69
+ .filter((f) => f !== "")
70
+ .map(abs);
71
+ return { tracked, untracked };
72
+ }
73
+ /** 1-based line numbers ADDED in the working tree vs mergeBase for one file. */
74
+ export function addedLines(root, mergeBase, file) {
75
+ const out = git(root, ["diff", "-U0", mergeBase, "--", file]) ?? "";
76
+ const lines = new Set();
77
+ for (const line of out.split("\n")) {
78
+ if (!line.startsWith("@@"))
79
+ continue;
80
+ const m = line.match(/\+(\d+)(?:,(\d+))?/);
81
+ if (m === null)
82
+ continue;
83
+ const start = Number(m[1]);
84
+ const len = m[2] === undefined ? 1 : Number(m[2]);
85
+ for (let i = 0; i < len; i++)
86
+ lines.add(start + i);
87
+ }
88
+ return lines;
89
+ }
90
+ //# sourceMappingURL=gitDiff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitDiff.js","sourceRoot":"","sources":["../src/gitDiff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runHookPostedit(): number;