@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.
- package/README.md +239 -0
- package/candidates/README.md +30 -0
- package/candidates/r10-as-cast.yml +24 -0
- package/candidates/r1d-scattered-numeric-const-in-component.yml +13 -0
- package/candidates/r2-helper-fn-in-tsx.yml +22 -0
- package/candidates/r3-hooks-outside-layout.yml +21 -0
- package/candidates/r6-jsx-comment-smell.yml +14 -0
- package/candidates/r7-raw-hsl-outside-tokens.yml +17 -0
- package/candidates/r8-inline-style-prop.yml +13 -0
- package/candidates/r9-optional-store-arg.yml +24 -0
- package/configs/biome.json +96 -0
- package/configs/tsconfig.base.json +21 -0
- package/dist/astGrep.d.ts +16 -0
- package/dist/astGrep.js +48 -0
- package/dist/astGrep.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +138 -0
- package/dist/cli.js.map +1 -0
- package/dist/deploy.d.ts +1 -0
- package/dist/deploy.js +34 -0
- package/dist/deploy.js.map +1 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.js +102 -0
- package/dist/doctor.js.map +1 -0
- package/dist/fileWalk.d.ts +2 -0
- package/dist/fileWalk.js +45 -0
- package/dist/fileWalk.js.map +1 -0
- package/dist/findings.d.ts +12 -0
- package/dist/findings.js +8 -0
- package/dist/findings.js.map +1 -0
- package/dist/gateRed.d.ts +1 -0
- package/dist/gateRed.js +93 -0
- package/dist/gateRed.js.map +1 -0
- package/dist/gitDiff.d.ts +18 -0
- package/dist/gitDiff.js +90 -0
- package/dist/gitDiff.js.map +1 -0
- package/dist/hookPostedit.d.ts +1 -0
- package/dist/hookPostedit.js +117 -0
- package/dist/hookPostedit.js.map +1 -0
- package/dist/hookStop.d.ts +1 -0
- package/dist/hookStop.js +56 -0
- package/dist/hookStop.js.map +1 -0
- package/dist/hookStopTest.d.ts +1 -0
- package/dist/hookStopTest.js +71 -0
- package/dist/hookStopTest.js.map +1 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.js +172 -0
- package/dist/init.js.map +1 -0
- package/dist/initTest.d.ts +1 -0
- package/dist/initTest.js +88 -0
- package/dist/initTest.js.map +1 -0
- package/dist/iterate.d.ts +9 -0
- package/dist/iterate.js +79 -0
- package/dist/iterate.js.map +1 -0
- package/dist/iterateTest.d.ts +1 -0
- package/dist/iterateTest.js +91 -0
- package/dist/iterateTest.js.map +1 -0
- package/dist/leakPatterns.d.ts +5 -0
- package/dist/leakPatterns.js +22 -0
- package/dist/leakPatterns.js.map +1 -0
- package/dist/leaks.d.ts +8 -0
- package/dist/leaks.js +211 -0
- package/dist/leaks.js.map +1 -0
- package/dist/leaksRed.d.ts +1 -0
- package/dist/leaksRed.js +62 -0
- package/dist/leaksRed.js.map +1 -0
- package/dist/metrics.d.ts +10 -0
- package/dist/metrics.js +113 -0
- package/dist/metrics.js.map +1 -0
- package/dist/metricsAggregate.d.ts +5 -0
- package/dist/metricsAggregate.js +78 -0
- package/dist/metricsAggregate.js.map +1 -0
- package/dist/metricsBaseline.d.ts +21 -0
- package/dist/metricsBaseline.js +88 -0
- package/dist/metricsBaseline.js.map +1 -0
- package/dist/metricsCollect.d.ts +8 -0
- package/dist/metricsCollect.js +295 -0
- package/dist/metricsCollect.js.map +1 -0
- package/dist/metricsCycle.d.ts +1 -0
- package/dist/metricsCycle.js +79 -0
- package/dist/metricsCycle.js.map +1 -0
- package/dist/metricsTypes.d.ts +102 -0
- package/dist/metricsTypes.js +2 -0
- package/dist/metricsTypes.js.map +1 -0
- package/dist/packagePaths.d.ts +7 -0
- package/dist/packagePaths.js +11 -0
- package/dist/packagePaths.js.map +1 -0
- package/dist/pluginApi.d.ts +24 -0
- package/dist/pluginApi.js +12 -0
- package/dist/pluginApi.js.map +1 -0
- package/dist/pluginResolve.d.ts +6 -0
- package/dist/pluginResolve.js +48 -0
- package/dist/pluginResolve.js.map +1 -0
- package/dist/screenScope.d.ts +21 -0
- package/dist/screenScope.js +81 -0
- package/dist/screenScope.js.map +1 -0
- package/dist/screenScopeTest.d.ts +1 -0
- package/dist/screenScopeTest.js +116 -0
- package/dist/screenScopeTest.js.map +1 -0
- package/dist/severity.d.ts +9 -0
- package/dist/severity.js +62 -0
- package/dist/severity.js.map +1 -0
- package/dist/severityTest.d.ts +1 -0
- package/dist/severityTest.js +63 -0
- package/dist/severityTest.js.map +1 -0
- package/dist/sourceLines.d.ts +9 -0
- package/dist/sourceLines.js +52 -0
- package/dist/sourceLines.js.map +1 -0
- package/dist/structure.d.ts +2 -0
- package/dist/structure.js +98 -0
- package/dist/structure.js.map +1 -0
- package/dist/testRules.d.ts +1 -0
- package/dist/testRules.js +285 -0
- package/dist/testRules.js.map +1 -0
- package/dist/textGrep.d.ts +2 -0
- package/dist/textGrep.js +52 -0
- package/dist/textGrep.js.map +1 -0
- package/dist/tomlTable.d.ts +2 -0
- package/dist/tomlTable.js +26 -0
- package/dist/tomlTable.js.map +1 -0
- package/dist/toolResolve.d.ts +18 -0
- package/dist/toolResolve.js +50 -0
- package/dist/toolResolve.js.map +1 -0
- package/dist/verify.d.ts +3 -0
- package/dist/verify.js +49 -0
- package/dist/verify.js.map +1 -0
- package/dist/verifyDiff.d.ts +18 -0
- package/dist/verifyDiff.js +126 -0
- package/dist/verifyDiff.js.map +1 -0
- package/dist/workspaces.d.ts +6 -0
- package/dist/workspaces.js +84 -0
- package/dist/workspaces.js.map +1 -0
- package/mise.toml +9 -0
- package/package.json +62 -0
- package/rules/__fixtures__/as-any-escape/bad.ts +8 -0
- package/rules/__fixtures__/as-any-escape/good.ts +14 -0
- package/rules/__fixtures__/catch-empty/bad.ts +7 -0
- package/rules/__fixtures__/catch-empty/good.ts +14 -0
- package/rules/__fixtures__/direct-store-import/bad.tsx +23 -0
- package/rules/__fixtures__/direct-store-import/good.tsx +34 -0
- package/rules/__fixtures__/direct-store-import/showcase/ignored.tsx +9 -0
- package/rules/__fixtures__/discriminator-ternary/bad.tsx +10 -0
- package/rules/__fixtures__/discriminator-ternary/good.tsx +23 -0
- package/rules/__fixtures__/hardcoded-url-in-component/components/bad.tsx +8 -0
- package/rules/__fixtures__/hardcoded-url-in-component/components/good.tsx +14 -0
- package/rules/__fixtures__/inline-map-row/bad.tsx +72 -0
- package/rules/__fixtures__/inline-map-row/good.tsx +51 -0
- package/rules/__fixtures__/instanceof-map-set/bad.ts +7 -0
- package/rules/__fixtures__/instanceof-map-set/good.ts +8 -0
- package/rules/__fixtures__/literal-union-in-component/components/bad.ts +8 -0
- package/rules/__fixtures__/literal-union-in-component/components/good.ts +23 -0
- package/rules/__fixtures__/missing-observer/bad.tsx +66 -0
- package/rules/__fixtures__/missing-observer/good.tsx +69 -0
- package/rules/__fixtures__/no-local-kit-clone/bad.ts +30 -0
- package/rules/__fixtures__/no-local-kit-clone/bad.tsx +30 -0
- package/rules/__fixtures__/no-local-kit-clone/good.ts +31 -0
- package/rules/__fixtures__/no-local-kit-clone/good.tsx +31 -0
- package/rules/__fixtures__/non-exhaustive-match/bad.ts +8 -0
- package/rules/__fixtures__/non-exhaustive-match/good.ts +14 -0
- package/rules/__fixtures__/page-store-bare-usememo/bad.ts +15 -0
- package/rules/__fixtures__/page-store-bare-usememo/bad.tsx +13 -0
- package/rules/__fixtures__/page-store-bare-usememo/good.ts +17 -0
- package/rules/__fixtures__/page-store-bare-usememo/good.tsx +14 -0
- package/rules/__fixtures__/screen-file-styling/bad.screen.tsx +10 -0
- package/rules/__fixtures__/screen-file-styling/good.screen.tsx +8 -0
- package/rules/__fixtures__/state-loading-boolean-shape/bad.ts +36 -0
- package/rules/__fixtures__/state-loading-boolean-shape/bad.tsx +36 -0
- package/rules/__fixtures__/state-loading-boolean-shape/good.ts +44 -0
- package/rules/__fixtures__/state-loading-boolean-shape/good.tsx +44 -0
- package/rules/__fixtures__/store-async-method/bad.ts +37 -0
- package/rules/__fixtures__/store-async-method/bad.tsx +37 -0
- package/rules/__fixtures__/store-async-method/good.ts +73 -0
- package/rules/__fixtures__/store-async-method/good.tsx +73 -0
- package/rules/__fixtures__/store-context-provider/bad.ts +16 -0
- package/rules/__fixtures__/store-context-provider/bad.tsx +24 -0
- package/rules/__fixtures__/store-context-provider/good.ts +13 -0
- package/rules/__fixtures__/store-context-provider/good.tsx +11 -0
- package/rules/__fixtures__/store-delegation-getter/bad.ts +30 -0
- package/rules/__fixtures__/store-delegation-getter/bad.tsx +30 -0
- package/rules/__fixtures__/store-delegation-getter/good.ts +65 -0
- package/rules/__fixtures__/store-delegation-getter/good.tsx +65 -0
- package/rules/__fixtures__/store-new-map/bad.ts +22 -0
- package/rules/__fixtures__/store-new-map/bad.tsx +22 -0
- package/rules/__fixtures__/store-new-map/good.ts +41 -0
- package/rules/__fixtures__/store-new-map/good.tsx +41 -0
- package/rules/__fixtures__/store-no-runinaction/bad.ts +67 -0
- package/rules/__fixtures__/store-no-runinaction/bad.tsx +67 -0
- package/rules/__fixtures__/store-no-runinaction/good.ts +39 -0
- package/rules/__fixtures__/store-no-runinaction/good.tsx +39 -0
- package/rules/__fixtures__/store-no-setinterval-poll/bad.ts +19 -0
- package/rules/__fixtures__/store-no-setinterval-poll/bad.tsx +19 -0
- package/rules/__fixtures__/store-no-setinterval-poll/good.ts +29 -0
- package/rules/__fixtures__/store-no-setinterval-poll/good.tsx +29 -0
- package/rules/__fixtures__/ts-pattern-dangling-match/bad.ts +20 -0
- package/rules/__fixtures__/ts-pattern-dangling-match/good.ts +35 -0
- package/rules/__fixtures__/ui-imports-app-store/src/ui/bad.tsx +13 -0
- package/rules/__fixtures__/ui-imports-app-store/src/ui/good.tsx +23 -0
- package/rules/__fixtures__/view-file-logic/bad.view.tsx +16 -0
- package/rules/__fixtures__/view-file-logic/good.view.tsx +7 -0
- package/rules/__fixtures__/view-imports-store/bad.view.tsx +13 -0
- package/rules/__fixtures__/view-imports-store/good.view.tsx +23 -0
- package/rules/__fixtures__/zod-optional-nullable/bad.ts +10 -0
- package/rules/__fixtures__/zod-optional-nullable/bad.tsx +17 -0
- package/rules/__fixtures__/zod-optional-nullable/good.ts +16 -0
- package/rules/__fixtures__/zod-optional-nullable/good.tsx +20 -0
- package/rules/as-any-escape-tsx.yml +53 -0
- package/rules/as-any-escape.yml +29 -0
- package/rules/catch-empty-tsx.yml +55 -0
- package/rules/catch-empty.yml +31 -0
- package/rules/classname-not-composed.yml +28 -0
- package/rules/classname-visual-identity.yml +34 -0
- package/rules/demo-mode-by-default-ts.yml +76 -0
- package/rules/demo-mode-by-default.yml +54 -0
- package/rules/direct-store-import.yml +118 -0
- package/rules/discriminator-ternary.yml +70 -0
- package/rules/hardcoded-url-in-component.yml +44 -0
- package/rules/inline-map-row.yml +107 -0
- package/rules/instanceof-map-set-tsx.yml +46 -0
- package/rules/instanceof-map-set.yml +22 -0
- package/rules/json-roundtrip.yml +9 -0
- package/rules/jsx-cond-and.yml +16 -0
- package/rules/jsx-ternary.yml +35 -0
- package/rules/kind-if-without-match.yml +13 -0
- package/rules/literal-union-in-component.yml +71 -0
- package/rules/match-bool-to-null.yml +15 -0
- package/rules/missing-observer.yml +189 -0
- package/rules/mobx-effect-observable-dep.yml +23 -0
- package/rules/mobx-effect-store-write.yml +21 -0
- package/rules/mobx-usestate-from-store.yml +20 -0
- package/rules/no-local-kit-clone-tsx.yml +104 -0
- package/rules/no-local-kit-clone.yml +93 -0
- package/rules/non-exhaustive-match.yml +34 -0
- package/rules/page-store-bare-usememo-tsx.yml +47 -0
- package/rules/page-store-bare-usememo.yml +43 -0
- package/rules/screen-file-styling.yml +29 -0
- package/rules/silent-default-return.yml +17 -0
- package/rules/state-loading-boolean-shape-tsx.yml +102 -0
- package/rules/state-loading-boolean-shape.yml +91 -0
- package/rules/store-async-method-tsx.yml +108 -0
- package/rules/store-async-method.yml +98 -0
- package/rules/store-context-provider-tsx.yml +68 -0
- package/rules/store-context-provider.yml +78 -0
- package/rules/store-delegation-getter-tsx.yml +104 -0
- package/rules/store-delegation-getter.yml +93 -0
- package/rules/store-new-map-tsx.yml +84 -0
- package/rules/store-new-map.yml +73 -0
- package/rules/store-no-runinaction-tsx.yml +71 -0
- package/rules/store-no-runinaction.yml +60 -0
- package/rules/store-no-setinterval-poll-tsx.yml +62 -0
- package/rules/store-no-setinterval-poll.yml +51 -0
- package/rules/ts-pattern-dangling-match-tsx.yml +41 -0
- package/rules/ts-pattern-dangling-match.yml +63 -0
- package/rules/ui-imports-app-store.yml +49 -0
- package/rules/view-file-logic.yml +53 -0
- package/rules/view-imports-store.yml +64 -0
- package/rules/zod-optional-nullable-tsx.yml +56 -0
- package/rules/zod-optional-nullable.yml +29 -0
- package/sgconfig.yml +8 -0
- package/structure/rules/component-decl.yml +48 -0
- 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
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/deploy.d.ts
ADDED
|
@@ -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"}
|
package/dist/doctor.d.ts
ADDED
|
@@ -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":"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"}
|
package/dist/fileWalk.js
ADDED
|
@@ -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;
|
package/dist/findings.js
ADDED
|
@@ -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 {};
|
package/dist/gateRed.js
ADDED
|
@@ -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>;
|
package/dist/gitDiff.js
ADDED
|
@@ -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;
|