@agentvibes/guardrails 0.1.0 → 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.
Files changed (68) hide show
  1. package/README.md +131 -5
  2. package/dist/astGrep.d.ts +3 -0
  3. package/dist/astGrep.js +15 -2
  4. package/dist/astGrep.js.map +1 -1
  5. package/dist/doctor.js +38 -2
  6. package/dist/doctor.js.map +1 -1
  7. package/dist/hookPostedit.js +18 -1
  8. package/dist/hookPostedit.js.map +1 -1
  9. package/dist/presetDrift.d.ts +31 -0
  10. package/dist/presetDrift.js +226 -0
  11. package/dist/presetDrift.js.map +1 -0
  12. package/dist/presetDriftTest.d.ts +1 -0
  13. package/dist/presetDriftTest.js +136 -0
  14. package/dist/presetDriftTest.js.map +1 -0
  15. package/dist/repoRules.d.ts +27 -0
  16. package/dist/repoRules.js +86 -0
  17. package/dist/repoRules.js.map +1 -0
  18. package/dist/repoRulesTest.d.ts +1 -0
  19. package/dist/repoRulesTest.js +122 -0
  20. package/dist/repoRulesTest.js.map +1 -0
  21. package/dist/screenScope.d.ts +16 -5
  22. package/dist/screenScope.js +66 -21
  23. package/dist/screenScope.js.map +1 -1
  24. package/dist/screenScopeTest.js +67 -7
  25. package/dist/screenScopeTest.js.map +1 -1
  26. package/dist/siblingCloneTest.d.ts +1 -0
  27. package/dist/siblingCloneTest.js +201 -0
  28. package/dist/siblingCloneTest.js.map +1 -0
  29. package/dist/testRules.js +77 -0
  30. package/dist/testRules.js.map +1 -1
  31. package/dist/twinCoverageTest.d.ts +1 -0
  32. package/dist/twinCoverageTest.js +262 -0
  33. package/dist/twinCoverageTest.js.map +1 -0
  34. package/dist/verify.d.ts +1 -1
  35. package/dist/verify.js +33 -8
  36. package/dist/verify.js.map +1 -1
  37. package/dist/verifyDiff.js +6 -5
  38. package/dist/verifyDiff.js.map +1 -1
  39. package/package.json +7 -3
  40. package/rules/__fixtures__/demo-mode-by-default/bad.ts +22 -0
  41. package/rules/__fixtures__/demo-mode-by-default/bad.tsx +22 -0
  42. package/rules/__fixtures__/demo-mode-by-default/good.ts +21 -0
  43. package/rules/__fixtures__/demo-mode-by-default/good.tsx +21 -0
  44. package/rules/__fixtures__/json-roundtrip/bad.ts +6 -0
  45. package/rules/__fixtures__/json-roundtrip/bad.tsx +8 -0
  46. package/rules/__fixtures__/json-roundtrip/good.ts +5 -0
  47. package/rules/__fixtures__/json-roundtrip/good.tsx +7 -0
  48. package/rules/__fixtures__/kind-if-without-match/bad.ts +17 -0
  49. package/rules/__fixtures__/kind-if-without-match/bad.tsx +18 -0
  50. package/rules/__fixtures__/kind-if-without-match/good.ts +14 -0
  51. package/rules/__fixtures__/kind-if-without-match/good.tsx +13 -0
  52. package/rules/__fixtures__/literal-union-in-component/components/bad.tsx +16 -0
  53. package/rules/__fixtures__/literal-union-in-component/components/good.tsx +25 -0
  54. package/rules/__fixtures__/non-exhaustive-match/bad.tsx +12 -0
  55. package/rules/__fixtures__/non-exhaustive-match/good.tsx +14 -0
  56. package/rules/__fixtures__/silent-default-return/bad.ts +9 -0
  57. package/rules/__fixtures__/silent-default-return/bad.tsx +10 -0
  58. package/rules/__fixtures__/silent-default-return/good.ts +17 -0
  59. package/rules/__fixtures__/silent-default-return/good.tsx +11 -0
  60. package/rules/demo-mode-by-default-ts.yml +17 -1
  61. package/rules/demo-mode-by-default.yml +17 -1
  62. package/rules/json-roundtrip-tsx.yml +26 -0
  63. package/rules/kind-if-without-match-tsx.yml +31 -0
  64. package/rules/literal-union-in-component-tsx.yml +81 -0
  65. package/rules/no-local-kit-clone-tsx.yml +16 -0
  66. package/rules/no-local-kit-clone.yml +16 -0
  67. package/rules/non-exhaustive-match-tsx.yml +48 -0
  68. package/rules/silent-default-return-tsx.yml +33 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presetDrift.js","sourceRoot":"","sources":["../src/presetDrift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,136 @@
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
+ import { presetDrift } from "./presetDrift.js";
7
+ // Fixtures for the preset-drift guard (is-9c7a78d7). The migration epic
8
+ // is-a70a5963 collapses 37 hand-rolled biome configs into one preset; this is
9
+ // what stops a repo re-growing its own afterwards.
10
+ //
11
+ // Both directions are executed, and so is the opt-in switch: a repo that has
12
+ // not yet migrated must SEE its drift and still exit 0, and the same repo with
13
+ // `[biome] preset = "enforced"` must exit 2. A guard that only ever ran in the
14
+ // enforced direction would not prove the unenforced one is quiet.
15
+ const CLEAN_TS = "export const two = 1 + 1;\n";
16
+ /** The config `guardrails init` writes, plus the two allowances. */
17
+ const CONFORMING = JSON.stringify({
18
+ $schema: "https://biomejs.dev/schemas/2.5.10/schema.json",
19
+ extends: ["@agentvibes/guardrails/biome"],
20
+ // Scoping its own paths is the repo's business, not the preset's.
21
+ files: { includes: ["**/*.ts", "!**/dist"] },
22
+ // A CLI has to print; this is the one rule an override may touch.
23
+ overrides: [{ includes: ["src/**"], linter: { rules: { suspicious: { noConsole: "off" } } } }],
24
+ }, null, 2);
25
+ const NO_EXTENDS = JSON.stringify({ formatter: { indentWidth: 4 } }, null, 2);
26
+ const OWN_KEYS = JSON.stringify({
27
+ extends: ["@agentvibes/guardrails/biome"],
28
+ formatter: { lineWidth: 80 },
29
+ linter: { rules: { style: { useConst: "off" } } },
30
+ assist: { enabled: false },
31
+ javascript: { formatter: { quoteStyle: "single" } },
32
+ json: { formatter: { indentWidth: 4 } },
33
+ }, null, 2);
34
+ const WIDE_OVERRIDE = JSON.stringify({
35
+ extends: ["@agentvibes/guardrails/biome"],
36
+ overrides: [
37
+ { includes: ["src/**"], linter: { rules: { style: { useConst: "off" } } } },
38
+ { includes: ["gen/**"], formatter: { enabled: false } },
39
+ ],
40
+ }, null, 2);
41
+ function makeRepo(opts) {
42
+ const dir = mkdtempSync(join(tmpdir(), "guardrails-preset-"));
43
+ writeFileSync(join(dir, "a.ts"), CLEAN_TS);
44
+ if (opts.biome !== undefined)
45
+ writeFileSync(join(dir, "biome.json"), opts.biome);
46
+ if (opts.secondConfig !== undefined) {
47
+ mkdirSync(join(dir, "packages", "web"), { recursive: true });
48
+ writeFileSync(join(dir, "packages", "web", "biome.json"), opts.secondConfig);
49
+ }
50
+ if (opts.enforced === true) {
51
+ mkdirSync(join(dir, ".agentvibes"), { recursive: true });
52
+ writeFileSync(join(dir, ".agentvibes", "project.toml"), '[biome]\npreset = "enforced"\n');
53
+ }
54
+ return { dir };
55
+ }
56
+ function run(cwd, command) {
57
+ const res = spawnSync("node", [join(packageRoot, "dist", "cli.js"), command], {
58
+ cwd,
59
+ encoding: "utf8",
60
+ });
61
+ if (res.error)
62
+ throw res.error;
63
+ return { status: res.status ?? -1, out: `${res.stdout}${res.stderr}` };
64
+ }
65
+ function expectStep(step, ok, detail) {
66
+ if (!ok) {
67
+ console.error(`FAIL preset-drift: ${step}\n${detail}`);
68
+ return false;
69
+ }
70
+ console.log(` ok preset-drift: ${step}`);
71
+ return true;
72
+ }
73
+ function main() {
74
+ const repos = [];
75
+ const make = (o) => {
76
+ const r = makeRepo(o);
77
+ repos.push(r);
78
+ return r;
79
+ };
80
+ try {
81
+ let ok = true;
82
+ // ── detection, key by key ────────────────────────────────────────────
83
+ const conforming = make({ biome: CONFORMING });
84
+ ok =
85
+ expectStep("a conforming config reports no drift (extends + files + noConsole override)", presetDrift(conforming.dir).length === 0, JSON.stringify(presetDrift(conforming.dir), null, 2)) && ok;
86
+ const noBiome = make({});
87
+ ok =
88
+ expectStep("a repo with no biome config at all reports no drift", presetDrift(noBiome.dir).length === 0, JSON.stringify(presetDrift(noBiome.dir), null, 2)) && ok;
89
+ const noExtends = make({ biome: NO_EXTENDS });
90
+ ok =
91
+ expectStep("(a) a config that does not extend the preset is drift", presetDrift(noExtends.dir).some((f) => f.key === "extends"), JSON.stringify(presetDrift(noExtends.dir), null, 2)) && ok;
92
+ const ownKeys = make({ biome: OWN_KEYS });
93
+ const ownDetail = presetDrift(ownKeys.dir).find((f) => f.key === "own-keys")?.detail ?? "";
94
+ ok =
95
+ expectStep("(b) formatter, linter, assist, javascript.formatter and json.formatter are all named", ["formatter", "linter", "assist", "javascript.formatter", "json.formatter"].every((k) => ownDetail.includes(k)), ownDetail) && ok;
96
+ const wide = make({ biome: WIDE_OVERRIDE });
97
+ const wideDetail = presetDrift(wide.dir).find((f) => f.key === "overrides")?.detail ?? "";
98
+ ok =
99
+ expectStep("(b) an override touching anything but suspicious.noConsole is drift", wideDetail.includes("overrides[0]") && wideDetail.includes("overrides[1].formatter"), wideDetail) && ok;
100
+ const second = make({ biome: CONFORMING, secondConfig: CONFORMING });
101
+ ok =
102
+ expectStep("(c) a second biome.json in the tree is drift even when both conform", presetDrift(second.dir).some((f) => f.key === "extra-config"), JSON.stringify(presetDrift(second.dir), null, 2)) && ok;
103
+ // ── the opt-in switch, through the CLI ───────────────────────────────
104
+ const unenforced = make({ biome: NO_EXTENDS });
105
+ const u = run(unenforced.dir, "verify");
106
+ ok =
107
+ expectStep("unenforced: verify SAYS the drift and still exits 0", u.status === 0 && u.out.includes("biome preset drift") && u.out.includes("Not gated here"), `exit ${u.status}\n${u.out}`) && ok;
108
+ const enforced = make({ biome: NO_EXTENDS, enforced: true });
109
+ const e = run(enforced.dir, "verify");
110
+ ok =
111
+ expectStep("enforced: the same repo exits 2 with the offending keys named", e.status === 2 && e.out.includes('biome.json has no "extends"'), `exit ${e.status}\n${e.out}`) && ok;
112
+ const enforcedOk = make({ biome: CONFORMING, enforced: true });
113
+ const eo = run(enforcedOk.dir, "verify");
114
+ ok =
115
+ expectStep("enforced + conforming: verify runs normally (exit 0, no drift line)", eo.status === 0 && !eo.out.includes("biome preset drift"), `exit ${eo.status}\n${eo.out}`) && ok;
116
+ const d = run(enforced.dir, "doctor");
117
+ ok =
118
+ expectStep("enforced: doctor exits 1 and prints the drift", d.status === 1 && d.out.includes("DRIFT (enforced here)"), `exit ${d.status}\n${d.out}`) && ok;
119
+ const du = run(unenforced.dir, "doctor");
120
+ ok =
121
+ expectStep("unenforced: doctor still REPORTS the drift, exit unchanged", du.out.includes("drift (not enforced here yet)"), `exit ${du.status}\n${du.out}`) && ok;
122
+ console.log("");
123
+ if (!ok) {
124
+ console.error("preset-drift assertions FAILED");
125
+ return 1;
126
+ }
127
+ console.log("preset-drift fixtures passed");
128
+ return 0;
129
+ }
130
+ finally {
131
+ for (const r of repos)
132
+ rmSync(r.dir, { recursive: true, force: true });
133
+ }
134
+ }
135
+ process.exit(main());
136
+ //# sourceMappingURL=presetDriftTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presetDriftTest.js","sourceRoot":"","sources":["../src/presetDriftTest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** A repo config that ast-grep could not load. */
2
+ export declare class RepoConfigError extends Error {
3
+ }
4
+ /**
5
+ * The consuming repo's own `sgconfig.yml`, or undefined when there is none.
6
+ *
7
+ * Returns undefined when `cwd` IS this package: scanning our own config twice
8
+ * would double every finding in our own fixtures and tests.
9
+ */
10
+ export declare function repoRuleConfig(cwd: string): string | undefined;
11
+ /** Every ast-grep config `verify` should scan with, canon first. */
12
+ export declare function ruleConfigs(cwd: string, canon: string): string[];
13
+ /**
14
+ * `ruleDirs:` as written in a repo config — for `doctor`, so a repo can see
15
+ * WHICH extra rule directories its config contributes rather than only that a
16
+ * config exists. Deliberately a line reader, not a YAML parse: this is a
17
+ * report, and a config shape it cannot read must not stop `doctor` from
18
+ * running (ast-grep is the authority on whether the file loads, and the strict
19
+ * scan in `astGrep.ts` is what reports that).
20
+ */
21
+ export declare function declaredRuleDirs(configPath: string): string[];
22
+ /** Findings from several configs, minus the duplicates the overlap produces. */
23
+ export declare function dedupeFindings<T extends {
24
+ rule: string;
25
+ file: string;
26
+ line: number;
27
+ }>(findings: T[]): T[];
@@ -0,0 +1,86 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { packageRoot } from "./packagePaths.js";
4
+ // Repo-local rules (is-f58cd6b4). `guardrails init` writes an `sgconfig.yml`
5
+ // into the consuming repo and the README tells people to add a second
6
+ // `ruleDir` for rules that encode ONE repo's convention — but `verify` and
7
+ // `verify-diff` only ever scanned with the PACKAGE's own sgconfig.yml, so
8
+ // those rules never ran. Both the intranet (`resource-match-exhaustive`) and
9
+ // DataSpool (`no-connector-fs`, `no-global-fetch`) worked around it by adding a
10
+ // bare `ast-grep scan -c sgconfig.yml` as a second gate stage.
11
+ //
12
+ // The failure mode is this package's least favourite: `verify` printed a
13
+ // finding count, and the count was of a strictly smaller rule set than the
14
+ // repo had asked for. Nothing said so.
15
+ //
16
+ // The fix scans with BOTH configs and merges. Not "the repo's config instead
17
+ // of ours": a repo whose sgconfig.yml forgets the canon `ruleDir` would then
18
+ // silently lose the entire canon, which is the same bug pointing the other
19
+ // way. The canon is always scanned; the repo's config is additive; duplicate
20
+ // findings (the repo's config almost always lists the canon dir too, so every
21
+ // canon finding arrives twice) are deduped by rule + file + line.
22
+ /** A repo config that ast-grep could not load. */
23
+ export class RepoConfigError extends Error {
24
+ }
25
+ /**
26
+ * The consuming repo's own `sgconfig.yml`, or undefined when there is none.
27
+ *
28
+ * Returns undefined when `cwd` IS this package: scanning our own config twice
29
+ * would double every finding in our own fixtures and tests.
30
+ */
31
+ export function repoRuleConfig(cwd) {
32
+ if (resolve(cwd) === resolve(packageRoot))
33
+ return undefined;
34
+ const path = join(cwd, "sgconfig.yml");
35
+ if (!existsSync(path))
36
+ return undefined;
37
+ return path;
38
+ }
39
+ /** Every ast-grep config `verify` should scan with, canon first. */
40
+ export function ruleConfigs(cwd, canon) {
41
+ const repo = repoRuleConfig(cwd);
42
+ return repo === undefined ? [canon] : [canon, repo];
43
+ }
44
+ /**
45
+ * `ruleDirs:` as written in a repo config — for `doctor`, so a repo can see
46
+ * WHICH extra rule directories its config contributes rather than only that a
47
+ * config exists. Deliberately a line reader, not a YAML parse: this is a
48
+ * report, and a config shape it cannot read must not stop `doctor` from
49
+ * running (ast-grep is the authority on whether the file loads, and the strict
50
+ * scan in `astGrep.ts` is what reports that).
51
+ */
52
+ export function declaredRuleDirs(configPath) {
53
+ const dirs = [];
54
+ let inRuleDirs = false;
55
+ for (const line of readFileSync(configPath, "utf8").split("\n")) {
56
+ if (/^ruleDirs:/.test(line)) {
57
+ inRuleDirs = true;
58
+ continue;
59
+ }
60
+ if (inRuleDirs) {
61
+ const item = /^\s+-\s*(.+?)\s*$/.exec(line);
62
+ if (item?.[1] !== undefined) {
63
+ dirs.push(item[1]);
64
+ continue;
65
+ }
66
+ if (line.trim() === "" || line.startsWith(" ") || line.startsWith("#"))
67
+ continue;
68
+ inRuleDirs = false;
69
+ }
70
+ }
71
+ return dirs;
72
+ }
73
+ /** Findings from several configs, minus the duplicates the overlap produces. */
74
+ export function dedupeFindings(findings) {
75
+ const seen = new Set();
76
+ const out = [];
77
+ for (const f of findings) {
78
+ const key = `${f.rule}\0${f.file}\0${f.line}`;
79
+ if (seen.has(key))
80
+ continue;
81
+ seen.add(key);
82
+ out.push(f);
83
+ }
84
+ return out;
85
+ }
86
+ //# sourceMappingURL=repoRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoRules.js","sourceRoot":"","sources":["../src/repoRules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,122 @@
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
+ // Regression fixtures for is-f58cd6b4 — `verify` ignored the consuming repo's
7
+ // own sgconfig.yml, so repo-local rules never ran and the finding count was of
8
+ // a smaller rule set than the repo had asked for, with nothing saying so.
9
+ //
10
+ // The fixture repo is the shape the bug was reported in: an `sgconfig.yml`
11
+ // listing BOTH the canon rule directory and a local one, a file that violates
12
+ // only the local rule, and a file that violates only a canon rule.
13
+ //
14
+ // Measured against the pre-fix build (HEAD's verify.ts, scanning only the
15
+ // package config): 4 of the 6 checks below fail — the local rule reports
16
+ // nothing, it gates nothing, a repo config without the canon ruleDir loses its
17
+ // local rules entirely, and an unparsable repo config reads as "no findings".
18
+ // The other two pass before and after; they pin that the fix did not cost the
19
+ // canon its coverage or start double-reporting it.
20
+ /** Violates only the repo-local rule; no canon rule matches `forbiddenCall()`. */
21
+ const LOCAL_ONLY = "export const boot = () => {\n forbiddenCall()\n}\n";
22
+ /** Violates only a canon rule (`catch-empty`, error tier). */
23
+ const CANON_ONLY = "export const run = () => {\n try { work() } catch (e) { }\n}\n";
24
+ const LOCAL_RULE = `id: no-forbidden-call
25
+ language: typescript
26
+ severity: error
27
+ message: |
28
+ This repo forbids forbiddenCall(). Repo-local rule, not part of the canon.
29
+ rule:
30
+ pattern: "forbiddenCall()"
31
+ `;
32
+ function verify(cwd, target = ".") {
33
+ const res = spawnSync("node", [join(packageRoot, "dist", "cli.js"), "verify", "--json", target], {
34
+ cwd,
35
+ encoding: "utf8",
36
+ });
37
+ if (res.error)
38
+ throw res.error;
39
+ const out = `${res.stdout}${res.stderr}`;
40
+ let findings = [];
41
+ try {
42
+ findings = JSON.parse(res.stdout).findings;
43
+ // ast-grep-ignore: catch-empty -- non-JSON stdout IS the answer for the exit-2 path this test asserts; the caller reads `status` and `out`, and every assertion that wanted findings fails with the raw output attached
44
+ }
45
+ catch {
46
+ findings = [];
47
+ }
48
+ return { status: res.status ?? -1, out, findings };
49
+ }
50
+ function makeRepo(sgconfig) {
51
+ const dir = mkdtempSync(join(tmpdir(), "guardrails-repo-rules-"));
52
+ mkdirSync(join(dir, ".ast-grep", "rules"), { recursive: true });
53
+ writeFileSync(join(dir, ".ast-grep", "rules", "no-forbidden-call.yml"), LOCAL_RULE);
54
+ writeFileSync(join(dir, "sgconfig.yml"), sgconfig);
55
+ writeFileSync(join(dir, "local.ts"), LOCAL_ONLY);
56
+ writeFileSync(join(dir, "canon.ts"), CANON_ONLY);
57
+ return dir;
58
+ }
59
+ /** The config `guardrails init` writes, with the local ruleDir uncommented. */
60
+ const BOTH_DIRS = `ruleDirs:
61
+ - ${join(packageRoot, "rules")}
62
+ - .ast-grep/rules
63
+ `;
64
+ /** A repo whose config declares ONLY its local rules — the canon must survive. */
65
+ const LOCAL_DIR_ONLY = `ruleDirs:
66
+ - .ast-grep/rules
67
+ `;
68
+ function expectStep(step, ok, detail) {
69
+ if (!ok) {
70
+ console.error(`FAIL repo-rules: ${step}\n${detail}`);
71
+ return false;
72
+ }
73
+ console.log(` ok repo-rules: ${step}`);
74
+ return true;
75
+ }
76
+ function main() {
77
+ const both = makeRepo(BOTH_DIRS);
78
+ const localOnly = makeRepo(LOCAL_DIR_ONLY);
79
+ const brokenConfig = makeRepo("ruleDirs: [\n");
80
+ try {
81
+ const r = verify(both);
82
+ const local = r.findings.filter((f) => f.rule === "no-forbidden-call");
83
+ const canon = r.findings.filter((f) => f.rule === "catch-empty");
84
+ let ok = expectStep("the repo's own rule reports (1 finding on local.ts)", local.length === 1 && local[0]?.file.endsWith("local.ts") === true, r.out);
85
+ // Scoped to local.ts so the exit code cannot be explained by the canon
86
+ // finding in canon.ts — this is the local rule gating, on its own.
87
+ const lonly = verify(both, "local.ts");
88
+ ok =
89
+ expectStep("a repo-local error-tier finding gates verify on its own (exit 1)", lonly.status === 1 && lonly.findings.every((f) => f.rule === "no-forbidden-call"), `exit ${lonly.status}\n${lonly.out}`) && ok;
90
+ ok =
91
+ expectStep("the canon still reports alongside it (catch-empty on canon.ts)", canon.length === 1, r.out) && ok;
92
+ // The overlap: the repo config lists the canon ruleDir too, so without
93
+ // dedupe every canon finding arrives once per config.
94
+ const keys = r.findings.map((f) => `${f.rule}\0${f.file}\0${f.line}`);
95
+ ok =
96
+ expectStep("the canon ruleDir listed in both configs is not double-reported", new Set(keys).size === keys.length, `${keys.length} findings, ${new Set(keys).size} distinct:\n${r.out}`) && ok;
97
+ // A repo config that forgets the canon must not silently lose the canon —
98
+ // that is this same bug pointing the other way.
99
+ const lr = verify(localOnly);
100
+ ok =
101
+ expectStep("a repo config without the canon ruleDir keeps canon coverage", lr.findings.some((f) => f.rule === "catch-empty") &&
102
+ lr.findings.some((f) => f.rule === "no-forbidden-call"), lr.out) && ok;
103
+ // A config ast-grep cannot parse must fail loudly (exit 2), never read as
104
+ // "no findings" — ast-grep exits 8 there and writes nothing to stdout.
105
+ const br = verify(brokenConfig);
106
+ ok =
107
+ expectStep("an unparsable repo config exits 2 with the reason, not 0 with silence", br.status === 2 && br.out.includes("ast-grep exited"), `exit ${br.status}\n${br.out}`) && ok;
108
+ console.log("");
109
+ if (!ok) {
110
+ console.error("repo-local rule assertions FAILED");
111
+ return 1;
112
+ }
113
+ console.log("repo-local rule fixtures passed");
114
+ return 0;
115
+ }
116
+ finally {
117
+ for (const d of [both, localOnly, brokenConfig])
118
+ rmSync(d, { recursive: true, force: true });
119
+ }
120
+ }
121
+ process.exit(main());
122
+ //# sourceMappingURL=repoRulesTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoRulesTest.js","sourceRoot":"","sources":["../src/repoRulesTest.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,15 @@
1
1
  import type { Finding } from "./findings.js";
2
2
  export declare class ScreenScopeError extends Error {
3
3
  }
4
+ export interface Scope {
5
+ /** The name in `appliesTo: not-<name>`. */
6
+ readonly name: string;
7
+ /** The key under `[verify]` in .agentvibes/project.toml. */
8
+ readonly tomlKey: string;
9
+ }
10
+ export declare const SCOPES: readonly Scope[];
11
+ /** scope name -> rule ids whose bundled definition declares `appliesTo: not-<scope>`. */
12
+ export declare function scopeExemptRuleIds(dir?: string): Map<string, Set<string>>;
4
13
  /** Rule ids whose bundled definition declares `appliesTo: not-screens`. */
5
14
  export declare function screenExemptRuleIds(dir?: string): Set<string>;
6
15
  /**
@@ -12,10 +21,12 @@ export declare function screenExemptRuleIds(dir?: string): Set<string>;
12
21
  * believes it has scoped them.
13
22
  */
14
23
  export declare function screensPattern(cwd: string): RegExp | undefined;
24
+ /** Every scope pattern the repo declares, keyed by scope name. */
25
+ export declare function scopePatterns(cwd: string): Map<string, RegExp>;
15
26
  /**
16
- * Drop findings from rules that do not apply to screens, in files the repo has
17
- * declared to BE screens. Everything else passes through untouched — including
18
- * those same rules outside the screen paths, which is the half that has to keep
19
- * working for the scoping to be worth anything.
27
+ * Drop findings from rules that do not apply to a scope, in files the repo has
28
+ * declared to BE that scope. Everything else passes through untouched —
29
+ * including those same rules outside the scoped paths, which is the half that
30
+ * has to keep working for the scoping to be worth anything.
20
31
  */
21
- export declare function dropScreenExempt(findings: readonly Finding[], screens: RegExp | undefined, exempt: ReadonlySet<string>): Finding[];
32
+ export declare function dropScopeExempt(findings: readonly Finding[], patterns: ReadonlyMap<string, RegExp>, exempt: ReadonlyMap<string, Set<string>>): Finding[];
@@ -31,11 +31,34 @@ import { readTomlTable } from "./tomlTable.js";
31
31
  //
32
32
  // Absent key = absent behaviour. A repo that says nothing is scanned exactly as
33
33
  // it was before this existed.
34
+ //
35
+ // GENERALISED (is-086a90ac). A second fact needed saying and the shape already
36
+ // fitted: `no-local-kit-clone` is error-tier and fires on the six declarations
37
+ // inside `@agentvibes/kit` itself — the canonical definitions the rule tells
38
+ // everyone else to import. Its own header names the fix ("if this IS the
39
+ // canonical definition … it belongs behind the path carve-outs") and its
40
+ // carve-out globs, written while the package did not exist yet, are
41
+ // `**/packages/kit/**` and `**/agentvibes-kit/**`: neither matches a standalone
42
+ // repo whose files verify sees as `src/resource/resource.ts`. No path glob can
43
+ // tell that tree from any other repo's `src/` — only the repo knows, so the
44
+ // repo says it:
45
+ //
46
+ // [verify]
47
+ // kit_source = "^src/"
48
+ //
49
+ // Adding a scope is two lines here plus `appliesTo: not-<scope>` in the rules
50
+ // that stand down for it. The registry is closed on purpose: an unknown scope
51
+ // key in a repo's config is a typo the repo cannot see, and the alternative — a
52
+ // free-form per-rule path map — is the growing allowlist this design refused.
34
53
  export class ScreenScopeError extends Error {
35
54
  }
36
- /** Rule ids whose bundled definition declares `appliesTo: not-screens`. */
37
- export function screenExemptRuleIds(dir = rulesDir) {
38
- const ids = new Set();
55
+ export const SCOPES = [
56
+ { name: "screens", tomlKey: "screens" },
57
+ { name: "kit-source", tomlKey: "kit_source" },
58
+ ];
59
+ /** scope name -> rule ids whose bundled definition declares `appliesTo: not-<scope>`. */
60
+ export function scopeExemptRuleIds(dir = rulesDir) {
61
+ const byScope = new Map(SCOPES.map((s) => [s.name, new Set()]));
39
62
  for (const f of readdirSync(dir)) {
40
63
  if (!f.endsWith(".yml"))
41
64
  continue;
@@ -43,10 +66,17 @@ export function screenExemptRuleIds(dir = rulesDir) {
43
66
  const id = text.match(/^id:\s*(\S+)/m)?.[1];
44
67
  if (id === undefined)
45
68
  continue;
46
- if (/^\s{2}appliesTo:\s*not-screens\s*$/m.test(text))
47
- ids.add(id);
69
+ for (const s of SCOPES) {
70
+ if (new RegExp(`^\\s{2}appliesTo:\\s*not-${s.name}\\s*$`, "m").test(text)) {
71
+ byScope.get(s.name)?.add(id);
72
+ }
73
+ }
48
74
  }
49
- return ids;
75
+ return byScope;
76
+ }
77
+ /** Rule ids whose bundled definition declares `appliesTo: not-screens`. */
78
+ export function screenExemptRuleIds(dir = rulesDir) {
79
+ return scopeExemptRuleIds(dir).get("screens") ?? new Set();
50
80
  }
51
81
  /**
52
82
  * The repo's `[verify] screens` pattern, or undefined when it declares none.
@@ -57,25 +87,40 @@ export function screenExemptRuleIds(dir = rulesDir) {
57
87
  * believes it has scoped them.
58
88
  */
59
89
  export function screensPattern(cwd) {
60
- const raw = readTomlTable(join(cwd, ".agentvibes", "project.toml"), "verify").screens;
61
- if (raw === undefined || raw.trim() === "")
62
- return undefined;
63
- try {
64
- return new RegExp(raw);
65
- }
66
- catch (e) {
67
- throw new ScreenScopeError(`[verify] screens = ${JSON.stringify(raw)} is not a valid regular expression (${e.message})`);
90
+ return scopePatterns(cwd).get("screens");
91
+ }
92
+ /** Every scope pattern the repo declares, keyed by scope name. */
93
+ export function scopePatterns(cwd) {
94
+ const table = readTomlTable(join(cwd, ".agentvibes", "project.toml"), "verify");
95
+ const out = new Map();
96
+ for (const s of SCOPES) {
97
+ const raw = table[s.tomlKey];
98
+ if (raw === undefined || raw.trim() === "")
99
+ continue;
100
+ try {
101
+ out.set(s.name, new RegExp(raw));
102
+ }
103
+ catch (e) {
104
+ throw new ScreenScopeError(`[verify] ${s.tomlKey} = ${JSON.stringify(raw)} is not a valid regular expression (${e.message})`);
105
+ }
68
106
  }
107
+ return out;
69
108
  }
70
109
  /**
71
- * Drop findings from rules that do not apply to screens, in files the repo has
72
- * declared to BE screens. Everything else passes through untouched — including
73
- * those same rules outside the screen paths, which is the half that has to keep
74
- * working for the scoping to be worth anything.
110
+ * Drop findings from rules that do not apply to a scope, in files the repo has
111
+ * declared to BE that scope. Everything else passes through untouched —
112
+ * including those same rules outside the scoped paths, which is the half that
113
+ * has to keep working for the scoping to be worth anything.
75
114
  */
76
- export function dropScreenExempt(findings, screens, exempt) {
77
- if (screens === undefined || exempt.size === 0)
115
+ export function dropScopeExempt(findings, patterns, exempt) {
116
+ if (patterns.size === 0)
78
117
  return [...findings];
79
- return findings.filter((f) => !(exempt.has(f.rule) && screens.test(f.file)));
118
+ return findings.filter((f) => {
119
+ for (const [scope, re] of patterns) {
120
+ if (exempt.get(scope)?.has(f.rule) === true && re.test(f.file))
121
+ return false;
122
+ }
123
+ return true;
124
+ });
80
125
  }
81
126
  //# sourceMappingURL=screenScope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenScope.js","sourceRoot":"","sources":["../src/screenScope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,8EAA8E;AAC9E,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,4CAA4C;AAC5C,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,8EAA8E;AAC9E,8DAA8D;AAC9D,EAAE;AACF,aAAa;AACb,8BAA8B;AAC9B,EAAE;AACF,yEAAyE;AACzE,+EAA+E;AAC/E,4EAA4E;AAC5E,4EAA4E;AAC5E,EAAE;AACF,cAAc;AACd,6BAA6B;AAC7B,EAAE;AACF,gFAAgF;AAChF,8BAA8B;AAC9B,MAAM,OAAO,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB,CAAC,MAAc,QAAQ;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,SAAS;QAClC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,EAAE,KAAK,SAAS;YAAE,SAAS;QAC/B,IAAI,qCAAqC,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC;IACtF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,gBAAgB,CACxB,sBAAsB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,uCAAwC,CAAW,CAAC,OAAO,GAAG,CACxG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA4B,EAC5B,OAA2B,EAC3B,MAA2B;IAE3B,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"screenScope.js","sourceRoot":"","sources":["../src/screenScope.ts"],"names":[],"mappings":"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"}