vigiles 27.2.0 → 28.0.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/dist/adapter-conformance.js +2 -2
- package/dist/adapters/claude-code/adapter.js +3 -2
- package/dist/adapters/claude-code/run-scripts.js +47 -8
- package/dist/adapters/codex/adapter.js +1 -2
- package/dist/cli-main.js +124 -25
- package/dist/core/adapter.d.ts +22 -1
- package/dist/core/hook-program.d.ts +43 -0
- package/dist/core/hook-program.js +32 -0
- package/dist/core/linters.js +3 -3
- package/dist/core/test-utils.d.ts +1 -2
- package/dist/core/test-utils.js +7 -9
- package/dist/core/tmp-root.d.ts +12 -0
- package/dist/core/tmp-root.js +59 -0
- package/dist/core/validate.d.ts +15 -1
- package/dist/core/validate.js +16 -2
- package/dist/eval.js +6 -5
- package/dist/harness-test.js +5 -5
- package/dist/hook-install.d.ts +53 -0
- package/dist/hook-install.js +60 -0
- package/dist/hook-runtime.d.ts +2 -2
- package/dist/hook-runtime.js +82 -63
- package/dist/load-hook.d.ts +1 -1
- package/dist/load-hook.js +2 -2
- package/dist/run-hook.js +17 -1
- package/dist/run-script.js +3 -3
- package/dist/sandbox.js +2 -2
- package/dist/scan-behavioral.js +2 -2
- package/dist/test.d.ts +1 -0
- package/dist/test.js +17 -2
- package/package.json +1 -1
|
@@ -13,13 +13,13 @@ exports.assertAdapterLoadsHooks = assertAdapterLoadsHooks;
|
|
|
13
13
|
* in their test suite. See `docs/authoring-an-adapter.md`.
|
|
14
14
|
*/
|
|
15
15
|
const node_fs_1 = require("node:fs");
|
|
16
|
-
const node_os_1 = require("node:os");
|
|
17
16
|
const node_path_1 = require("node:path");
|
|
18
17
|
const compile_js_1 = require("./core/compile.js");
|
|
19
18
|
const spec_js_1 = require("./core/spec.js");
|
|
20
19
|
const plugin_loader_js_1 = require("./plugin-loader.js");
|
|
21
20
|
const vocabulary_consistency_js_1 = require("./core/vocabulary-consistency.js");
|
|
22
21
|
const event_capability_js_1 = require("./core/event-capability.js");
|
|
22
|
+
const tmp_root_js_1 = require("./core/tmp-root.js");
|
|
23
23
|
/** Check an adapter against the port contracts; returns the (possibly empty) failure list. */
|
|
24
24
|
function checkAdapterConformance(adapter) {
|
|
25
25
|
const failures = [];
|
|
@@ -166,7 +166,7 @@ function assertHarnessTestable(adapter) {
|
|
|
166
166
|
* run with zero hooks. Does filesystem IO, so it's a separate opt-in assert.
|
|
167
167
|
*/
|
|
168
168
|
function assertAdapterLoadsHooks(adapter) {
|
|
169
|
-
const dir = (0,
|
|
169
|
+
const dir = (0, tmp_root_js_1.makeTmpDir)("conformance");
|
|
170
170
|
try {
|
|
171
171
|
const settingsAbs = (0, node_path_1.join)(dir, adapter.layout.settingsPath);
|
|
172
172
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(settingsAbs), { recursive: true });
|
|
@@ -14,7 +14,6 @@ const layout_js_1 = require("./layout.js");
|
|
|
14
14
|
const runtime_js_1 = require("./runtime.js");
|
|
15
15
|
const hook_protocol_js_1 = require("./hook-protocol.js");
|
|
16
16
|
const model_mock_js_1 = require("./model-mock.js");
|
|
17
|
-
const harness_test_js_1 = require("../../harness-test.js");
|
|
18
17
|
exports.claudeCodeAdapter = {
|
|
19
18
|
name: "claude-code",
|
|
20
19
|
// The reference harness: every tier. Mockable transport (Anthropic SSE) and
|
|
@@ -30,7 +29,9 @@ exports.claudeCodeAdapter = {
|
|
|
30
29
|
runtime: runtime_js_1.claudeCodeRuntime,
|
|
31
30
|
hookProtocol: hook_protocol_js_1.claudeCodeHookProtocol,
|
|
32
31
|
modelMock: model_mock_js_1.claudeCodeModelMock,
|
|
33
|
-
|
|
32
|
+
// Imported inside the thunk, not at the top: a top-level import runs at module
|
|
33
|
+
// init and would pull the whole test/compiler graph back in.
|
|
34
|
+
harnessTestDriver: async () => (await import("../../harness-test.js")).claudeCodeDriver,
|
|
34
35
|
detect(root) {
|
|
35
36
|
// Most specific signal wins: a plugin manifest (3) > repo settings (2) >
|
|
36
37
|
// a bare CLAUDE.md (1, weak — many tools also read it / AGENTS.md).
|
|
@@ -26,7 +26,6 @@ const node_os_1 = require("node:os");
|
|
|
26
26
|
const node_path_1 = require("node:path");
|
|
27
27
|
const node_url_1 = require("node:url");
|
|
28
28
|
const node_fs_1 = require("node:fs");
|
|
29
|
-
const node_os_2 = require("node:os");
|
|
30
29
|
const glob_1 = require("glob");
|
|
31
30
|
const check_count_js_1 = require("../../check-count.js");
|
|
32
31
|
/**
|
|
@@ -64,8 +63,21 @@ exports.SKIP_EXIT_CODE = 77;
|
|
|
64
63
|
function statusFor(code, checks, output) {
|
|
65
64
|
if (code === exports.SKIP_EXIT_CODE)
|
|
66
65
|
return "skip";
|
|
66
|
+
// 🔴 `checks === undefined` GUARDS THE TEXT MATCH, and it is the load-bearing
|
|
67
|
+
// half. A script that REPORTED a count executed: the counter is written by an
|
|
68
|
+
// exit handler that exists only once the module was linked and run
|
|
69
|
+
// (`check-count.ts`), so the count is a STRUCTURAL fact about the child, while
|
|
70
|
+
// `didNotLoad` is a guess about its text.
|
|
71
|
+
//
|
|
72
|
+
// MEASURED: `statusFor(1, 3, "<hook stderr: Cannot find module …>\nAssertionError")`
|
|
73
|
+
// returned `"skip"` and the run exited 0 — a harness that ran, recorded three
|
|
74
|
+
// checks and FAILED an assertion, reported as skipped. That is not an exotic
|
|
75
|
+
// input: vigiles harnesses drive hooks and print their transcripts, so a
|
|
76
|
+
// loader phrase in the output is ordinary EVIDENCE about the thing under test,
|
|
77
|
+
// not a diagnosis of the harness. Watching the child from outside cannot tell
|
|
78
|
+
// those apart. The count can, and it was already in hand.
|
|
67
79
|
if (code !== 0)
|
|
68
|
-
return didNotLoad(output) ? "skip" : "fail";
|
|
80
|
+
return checks === undefined && didNotLoad(output) ? "skip" : "fail";
|
|
69
81
|
return checks === 0 ? "vacuous" : "pass";
|
|
70
82
|
}
|
|
71
83
|
/**
|
|
@@ -83,10 +95,17 @@ function statusFor(code, checks, output) {
|
|
|
83
95
|
* 'recordCheck' not found`, and the ledger dropped from 48 records to 34 and from
|
|
84
96
|
* 47 to 33. Nothing about those surfaces had changed — the machine had.
|
|
85
97
|
*
|
|
86
|
-
* ⚠️ This does NOT make a broken environment quiet
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* measurement taken on a machine
|
|
98
|
+
* ⚠️ This does NOT make a broken environment quiet: a script classified here
|
|
99
|
+
* fails the run by default (`cli-main.ts`, right after `anyFailed`), because a
|
|
100
|
+
* skip the AUTHOR never declared is not a skip. What this classification buys is
|
|
101
|
+
* only that a machine problem may not delete a measurement taken on a machine
|
|
102
|
+
* that worked.
|
|
103
|
+
*
|
|
104
|
+
* 🔴 That default is new, and the sentence it replaces was false. It read
|
|
105
|
+
* «`--no-skip` — which this repo's own CI passes — still fails the run».
|
|
106
|
+
* Measured: `--no-skip` appears ZERO times under `.github/`, `package.json`,
|
|
107
|
+
* `scripts/` and `.claude/`; CI passes `--min=14` and nothing else. The
|
|
108
|
+
* safety net the non-fatal classification leaned on was never strung.
|
|
90
109
|
*
|
|
91
110
|
* Deliberately literal, and only the loader's own vocabulary: these strings come
|
|
92
111
|
* from Node's module resolution, not from user code. A test that legitimately
|
|
@@ -99,6 +118,13 @@ function didNotLoad(output) {
|
|
|
99
118
|
output.includes("Cannot find package") ||
|
|
100
119
|
output.includes("Cannot find module") ||
|
|
101
120
|
/SyntaxError: Named export '[^']*' not found/.test(output) ||
|
|
121
|
+
// Same event, ESM spelling. Node phrases a missing named export one way for
|
|
122
|
+
// a CommonJS target and another for an ES module, and only the first was
|
|
123
|
+
// listed — so the 2026-08-20 class below still RETRACTED coverage whenever
|
|
124
|
+
// the dependency happened to be ESM. Measured on Node 22:
|
|
125
|
+
// CJS: SyntaxError: Named export 'recordCheck' not found. The requested module …
|
|
126
|
+
// ESM: SyntaxError: The requested module './x.mjs' does not provide an export named 'recordCheck'
|
|
127
|
+
/SyntaxError: The requested module '[^']*' does not provide an export named/.test(output) ||
|
|
102
128
|
output.includes("ERR_UNSUPPORTED_DIR_IMPORT") ||
|
|
103
129
|
output.includes("ERR_PACKAGE_PATH_NOT_EXPORTED"));
|
|
104
130
|
}
|
|
@@ -118,6 +144,7 @@ var ts_runner_caps_js_1 = require("../../ts-runner-caps.js");
|
|
|
118
144
|
Object.defineProperty(exports, "detectNodeCaps", { enumerable: true, get: function () { return ts_runner_caps_js_1.detectNodeCaps; } });
|
|
119
145
|
Object.defineProperty(exports, "canRunTypeScript", { enumerable: true, get: function () { return ts_runner_caps_js_1.canRunTypeScript; } });
|
|
120
146
|
const ts_runner_caps_js_2 = require("../../ts-runner-caps.js");
|
|
147
|
+
const tmp_root_js_1 = require("../../core/tmp-root.js");
|
|
121
148
|
/**
|
|
122
149
|
* The `node` argv (after the binary) to run a single script. Plain JS runs
|
|
123
150
|
* directly; a TypeScript script picks `tsx` when available, else Node's native
|
|
@@ -159,7 +186,13 @@ function discoverScripts(patterns, defaultGlob, cwd, ignore) {
|
|
|
159
186
|
const globs = patterns.length > 0 ? patterns : [defaultGlob];
|
|
160
187
|
const found = new Set();
|
|
161
188
|
for (const p of globs) {
|
|
162
|
-
|
|
189
|
+
// 🔴 `isFile`, not `existsSync`: a DIRECTORY exists too. `vigiles test .`
|
|
190
|
+
// therefore passed `.` through as a script, `spawn("node", ["."])` died with
|
|
191
|
+
// Node's `ERR_UNSUPPORTED_DIR_IMPORT` stack, and the classifier downstream
|
|
192
|
+
// read that stack as "did not load" — a crash reported as a skip, exit 0.
|
|
193
|
+
// A directory now contributes no files, so the caller's own loud
|
|
194
|
+
// nothing-matched path owns the message (see `cli-main.ts`).
|
|
195
|
+
if ((0, node_fs_1.statSync)((0, node_path_1.resolve)(cwd, p), { throwIfNoEntry: false })?.isFile()) {
|
|
163
196
|
found.add(p);
|
|
164
197
|
continue;
|
|
165
198
|
}
|
|
@@ -174,10 +207,16 @@ function discoverScripts(patterns, defaultGlob, cwd, ignore) {
|
|
|
174
207
|
// `test-coverage.ts` and `cli.ts` both pass `dot: true` with comments saying why, and
|
|
175
208
|
// `test-coverage.test.ts` records "glob without `dot:true` never found it and the surface
|
|
176
209
|
// looked untested". Coverage learned it; the runner did not.
|
|
210
|
+
// 🔴 `nodir: true` for the same reason as the `isFile` guard above, and the
|
|
211
|
+
// guard alone was NOT enough — caught by its own test. A pattern that names
|
|
212
|
+
// an existing directory (`sub`, `.`) skips the fast path and then comes back
|
|
213
|
+
// out of the globber, because a directory matches a glob perfectly well.
|
|
214
|
+
// Asked of the globber rather than filtered afterwards: it already knows.
|
|
177
215
|
for (const m of (0, glob_1.globSync)(p, {
|
|
178
216
|
cwd,
|
|
179
217
|
ignore: [...ignore],
|
|
180
218
|
dot: true,
|
|
219
|
+
nodir: true,
|
|
181
220
|
})) {
|
|
182
221
|
found.add(m);
|
|
183
222
|
}
|
|
@@ -219,7 +258,7 @@ function readCheckReport(path) {
|
|
|
219
258
|
*/
|
|
220
259
|
async function runScripts(files, cwd, env = {}, opts = {}) {
|
|
221
260
|
const caps = (0, ts_runner_caps_js_2.detectNodeCaps)(cwd);
|
|
222
|
-
const countDir = (0,
|
|
261
|
+
const countDir = (0, tmp_root_js_1.makeTmpDir)("checks");
|
|
223
262
|
// 🔴 THE DEFAULT IS DECIDED BY `entry`, NOT BY A FLAG, because the two commands
|
|
224
263
|
// that share this runner have OPPOSITE right answers and the caller already
|
|
225
264
|
// distinguishes them:
|
|
@@ -20,7 +20,6 @@ const layout_js_1 = require("./layout.js");
|
|
|
20
20
|
const runtime_js_1 = require("./runtime.js");
|
|
21
21
|
const hook_protocol_js_1 = require("./hook-protocol.js");
|
|
22
22
|
const model_mock_js_1 = require("./model-mock.js");
|
|
23
|
-
const driver_js_1 = require("./driver.js");
|
|
24
23
|
exports.codexAdapter = {
|
|
25
24
|
name: "codex",
|
|
26
25
|
// Full convergence with Claude Code: mockable (Responses SSE) + shell hooks
|
|
@@ -38,7 +37,7 @@ exports.codexAdapter = {
|
|
|
38
37
|
runtime: runtime_js_1.codexRuntime,
|
|
39
38
|
hookProtocol: hook_protocol_js_1.codexHookProtocol,
|
|
40
39
|
modelMock: model_mock_js_1.codexModelMock,
|
|
41
|
-
harnessTestDriver:
|
|
40
|
+
harnessTestDriver: async () => (await import("./driver.js")).codexDriver,
|
|
42
41
|
detect(root) {
|
|
43
42
|
// A `.codex/config.toml` is a strong signal; a bare AGENTS.md is weak (many
|
|
44
43
|
// harnesses read it). (Unused while unregistered — kept for symmetry.)
|
package/dist/cli-main.js
CHANGED
|
@@ -4936,10 +4936,19 @@ async function handleRunScripts(kind, args, restArgs, excludes) {
|
|
|
4936
4936
|
// VALUE cannot tell them apart — only the flag's presence can. (Caught by a control:
|
|
4937
4937
|
// the first version read `minRequired === 0` and made `--min=0` do nothing.)
|
|
4938
4938
|
if (restArgs.length > 0 && minFlag === undefined) {
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4939
|
+
// A DIRECTORY is the one stale-looking target whose cause we actually know,
|
|
4940
|
+
// so say it instead of listing the three guesses. `vigiles test .` used to
|
|
4941
|
+
// reach `spawn("node", ["."])` and surface Node's module-resolution stack;
|
|
4942
|
+
// the generic message above would now be true but unhelpful.
|
|
4943
|
+
const dirs = restArgs.filter((a) => (0, node_fs_1.lstatSync)(a, { throwIfNoEntry: false })?.isDirectory() === true);
|
|
4944
|
+
console.error(dirs.length > 0
|
|
4945
|
+
? `✗ vigiles ${kind}: ${dirs.join(", ")} ${dirs.length === 1 ? "is a directory" : "are directories"} — ` +
|
|
4946
|
+
`pass a file, or a glob like "${defaultGlob}".\n` +
|
|
4947
|
+
` A directory is not a script; nothing ran.`
|
|
4948
|
+
: `✗ vigiles ${kind}: ${String(restArgs.length)} target(s) given and NOTHING matched — ` +
|
|
4949
|
+
`${restArgs.join(", ")}\n` +
|
|
4950
|
+
` Nothing ran. A stale path, a wrong glob, or a moved file all look like this.\n` +
|
|
4951
|
+
` If an empty match is expected here, say so with --min=0.`);
|
|
4943
4952
|
process.exit(1);
|
|
4944
4953
|
}
|
|
4945
4954
|
console.log(`No ${defaultGlob} files found.`);
|
|
@@ -5002,6 +5011,35 @@ async function handleRunScripts(kind, args, restArgs, excludes) {
|
|
|
5002
5011
|
console.log("\n" + (0, run_scripts_js_1.formatScriptSummary)(results));
|
|
5003
5012
|
if ((0, run_scripts_js_1.anyFailed)(results))
|
|
5004
5013
|
process.exit(1);
|
|
5014
|
+
// 🔴 A SKIP THE AUTHOR NEVER DECLARED IS NOT A SKIP — and the discriminator was
|
|
5015
|
+
// already sitting in the result. `skip()` exits 77 (`SKIP_EXIT_CODE`); a script
|
|
5016
|
+
// the runtime could not evaluate exits with whatever the loader gave it, 1 in
|
|
5017
|
+
// practice. Both are classified `"skip"` so that neither RETRACTS coverage —
|
|
5018
|
+
// which is right, a file that did not run proved nothing either way — but only
|
|
5019
|
+
// the declared one is a reason to stay green.
|
|
5020
|
+
//
|
|
5021
|
+
// Reported as #243: `vigiles test .` printed a resolver stack over a `⊘`, said
|
|
5022
|
+
// `0 passed, 1 skipped`, and exited 0. Downstream a consumer's README shipped
|
|
5023
|
+
// that exact command as its first setup step, so a new reader's suite silently
|
|
5024
|
+
// never ran. `--no-skip` would have caught it and is not the default; `--min=1`
|
|
5025
|
+
// does not, because it counts files MATCHED, not scripts executed.
|
|
5026
|
+
//
|
|
5027
|
+
// This is deliberately NOT a fifth `ScriptStatus`. Coverage retraction reads the
|
|
5028
|
+
// status as a bare STRING (`executedScripts`, `coverage-artifact.ts`, whose
|
|
5029
|
+
// parameter is typed `string`), so a new member would start retracting silently
|
|
5030
|
+
// with no type error — breaking the one property the classification exists to
|
|
5031
|
+
// protect.
|
|
5032
|
+
const notEvaluated = results.filter((r) => r.status === "skip" && r.code !== run_scripts_js_1.SKIP_EXIT_CODE);
|
|
5033
|
+
if (notEvaluated.length > 0) {
|
|
5034
|
+
console.error(`\n✗ vigiles ${kind}: ${String(notEvaluated.length)} script(s) never ran — the runtime could not load them:\n` +
|
|
5035
|
+
notEvaluated
|
|
5036
|
+
.map((r) => ` ${r.file} (exit ${String(r.code)})`)
|
|
5037
|
+
.join("\n") +
|
|
5038
|
+
`\n Their previous coverage is kept, because a script that did not run retracts nothing.\n` +
|
|
5039
|
+
` If a missing dependency is expected here, import it dynamically and call skip() — ` +
|
|
5040
|
+
`a declared skip stays green.`);
|
|
5041
|
+
process.exit(1);
|
|
5042
|
+
}
|
|
5005
5043
|
// `--no-skip`: in a context that ASSERTS the capability is present (a CI job),
|
|
5006
5044
|
// a skipped tier is untested surface — fail loudly instead of passing green.
|
|
5007
5045
|
if (args.includes("--no-skip") && results.some((r) => r.status === "skip")) {
|
|
@@ -5214,6 +5252,45 @@ function annotateLintForGitHub(report, flags) {
|
|
|
5214
5252
|
ghAnnotate("warning", `${String(report.duplicatePairs)} near-duplicate rule pair(s) detected — consider merging`);
|
|
5215
5253
|
}
|
|
5216
5254
|
}
|
|
5255
|
+
/**
|
|
5256
|
+
* The project root a `hook-runtime` rail acts on — NEVER `process.cwd()` first.
|
|
5257
|
+
*
|
|
5258
|
+
* These rails are a SECOND execution path, wired by hand into a hooks config
|
|
5259
|
+
* (`npx vigiles hook-runtime <kind>`), and every one of them used to ask
|
|
5260
|
+
* `process.cwd()` where the project was. The hook process has no stable cwd: a
|
|
5261
|
+
* git worktree, or a session that has `cd`-ed into a subdirectory, stands
|
|
5262
|
+
* somewhere the project's files are not, and then the state store writes its
|
|
5263
|
+
* marker beside the wrong repo and `.vigiles/*.json` is simply not found. The
|
|
5264
|
+
* failure is SILENT in the permissive direction — no gates loaded reads exactly
|
|
5265
|
+
* like a project that declared none.
|
|
5266
|
+
*
|
|
5267
|
+
* Same order the compiled runtime uses (`projectRootOf`, core/hook-program.ts):
|
|
5268
|
+
* `$CLAUDE_PROJECT_DIR` first — the harness resolved the hook's own path against
|
|
5269
|
+
* it — then the event's own `cwd`, which Claude Code puts in every hook payload.
|
|
5270
|
+
* `process.cwd()` stays as the documented LAST resort, for the rails a human or
|
|
5271
|
+
* the model invokes as a plain command with no event and no env to go on.
|
|
5272
|
+
*
|
|
5273
|
+
* Pass the parsed event wherever stdin was already read; the handlers that take
|
|
5274
|
+
* only an argument pass nothing and get the env answer.
|
|
5275
|
+
*/
|
|
5276
|
+
function runtimeRoot(event = {}) {
|
|
5277
|
+
return (0, hook_program_js_1.projectRootOf)(event, process.env) ?? process.cwd();
|
|
5278
|
+
}
|
|
5279
|
+
/**
|
|
5280
|
+
* The hook payload as a root SOURCE — `{}` when stdin was absent or malformed,
|
|
5281
|
+
* which {@link runtimeRoot} reads as "this event offers no root" and falls
|
|
5282
|
+
* through. Deliberately separate from each handler's own parse: a rail that
|
|
5283
|
+
* cannot understand its event still knows where the project is, and a rail that
|
|
5284
|
+
* only wants `tool_name` should not have to widen its own type to say so.
|
|
5285
|
+
*/
|
|
5286
|
+
function eventRoot(raw) {
|
|
5287
|
+
try {
|
|
5288
|
+
return JSON.parse(raw);
|
|
5289
|
+
}
|
|
5290
|
+
catch {
|
|
5291
|
+
return {};
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5217
5294
|
/**
|
|
5218
5295
|
* Run a compiled skill's deterministic gate ladder: execute each step gate in
|
|
5219
5296
|
* order (short-circuiting on the first failure), then the result gate. This is
|
|
@@ -5226,7 +5303,8 @@ function runSkillCommand(target) {
|
|
|
5226
5303
|
console.error("Usage: vigiles hook-runtime run-skill <SKILL.md>");
|
|
5227
5304
|
process.exit(2);
|
|
5228
5305
|
}
|
|
5229
|
-
const
|
|
5306
|
+
const root = runtimeRoot();
|
|
5307
|
+
const path = (0, node_path_1.resolve)(root, target);
|
|
5230
5308
|
if (!(0, node_fs_1.existsSync)(path)) {
|
|
5231
5309
|
console.error(`Not found: ${target}`);
|
|
5232
5310
|
process.exit(2);
|
|
@@ -5237,7 +5315,7 @@ function runSkillCommand(target) {
|
|
|
5237
5315
|
return;
|
|
5238
5316
|
}
|
|
5239
5317
|
console.log(`Running gate ladder for ${target}:\n`);
|
|
5240
|
-
const report = (0, skill_runtime_js_1.runSkillGates)(gates,
|
|
5318
|
+
const report = (0, skill_runtime_js_1.runSkillGates)(gates, root);
|
|
5241
5319
|
for (const r of report.results) {
|
|
5242
5320
|
const label = r.at === "result" ? "result" : `step ${String(r.at)}`;
|
|
5243
5321
|
console.log(` ${r.ok ? "✓" : "✗"} ${label} — ${(0, skill_runtime_js_1.gateLabel)(r.gate)}`);
|
|
@@ -5265,11 +5343,12 @@ function runSkillCommand(target) {
|
|
|
5265
5343
|
* feeds the message back to the model; exit 0 allows it and clears the marker.
|
|
5266
5344
|
*/
|
|
5267
5345
|
function skillHookCommand() {
|
|
5268
|
-
const
|
|
5346
|
+
const root = runtimeRoot();
|
|
5347
|
+
const decision = (0, skill_runtime_js_1.evaluateStopHook)(root);
|
|
5269
5348
|
if (decision.allow) {
|
|
5270
5349
|
if (decision.message)
|
|
5271
5350
|
console.log(decision.message);
|
|
5272
|
-
(0, skill_runtime_js_1.clearActiveSkill)(
|
|
5351
|
+
(0, skill_runtime_js_1.clearActiveSkill)(root);
|
|
5273
5352
|
return;
|
|
5274
5353
|
}
|
|
5275
5354
|
console.error(decision.message);
|
|
@@ -5281,12 +5360,20 @@ function skillStartCommand(target) {
|
|
|
5281
5360
|
console.error("Usage: vigiles hook-runtime skill-start <SKILL.md>");
|
|
5282
5361
|
process.exit(2);
|
|
5283
5362
|
}
|
|
5284
|
-
|
|
5363
|
+
const root = runtimeRoot();
|
|
5364
|
+
(0, skill_runtime_js_1.setActiveSkill)(root, target);
|
|
5285
5365
|
// Record the fire in the flight recorder: the skill NAME is the parent dir of
|
|
5286
5366
|
// its SKILL.md (skills/<name>/SKILL.md), falling back to the raw target.
|
|
5287
5367
|
const parts = target.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
5288
5368
|
const name = parts.length >= 2 ? parts[parts.length - 2] : (parts[0] ?? target);
|
|
5289
|
-
|
|
5369
|
+
// The SAME root the decision above used. `appendObservation` defaults to
|
|
5370
|
+
// `process.cwd()`, which is correct as a library default and wrong here: a
|
|
5371
|
+
// hook does not run with a stable cwd, so the decision would land in the
|
|
5372
|
+
// project while its record landed beside whatever directory the process
|
|
5373
|
+
// happened to stand in. A ledger split across two directories is not untidy,
|
|
5374
|
+
// it is wrong in a way that reads as normal — the file in the project looks
|
|
5375
|
+
// complete, and nobody notices a flight recorder that is short.
|
|
5376
|
+
(0, observe_js_1.appendObservation)({ kind: "skill", name, fired: true }, root);
|
|
5290
5377
|
console.log(`Active skill: ${target}`);
|
|
5291
5378
|
}
|
|
5292
5379
|
/**
|
|
@@ -5319,7 +5406,7 @@ function skillToolHookCommand() {
|
|
|
5319
5406
|
}
|
|
5320
5407
|
if (!tool)
|
|
5321
5408
|
return;
|
|
5322
|
-
const decision = (0, skill_runtime_js_1.evaluateSkillPreToolUse)(
|
|
5409
|
+
const decision = (0, skill_runtime_js_1.evaluateSkillPreToolUse)(runtimeRoot(eventRoot(raw)), tool, command);
|
|
5323
5410
|
if (!decision.allow) {
|
|
5324
5411
|
console.error(decision.message);
|
|
5325
5412
|
process.exit(2);
|
|
@@ -5355,7 +5442,7 @@ function agentHookCommand() {
|
|
|
5355
5442
|
catch {
|
|
5356
5443
|
/* malformed input → no tool, allow */
|
|
5357
5444
|
}
|
|
5358
|
-
const cwd =
|
|
5445
|
+
const cwd = runtimeRoot(eventRoot(raw));
|
|
5359
5446
|
// EXPERIMENTAL (parked P3 — do NOT auto-wire). The spawn/SubagentStop bracketing
|
|
5360
5447
|
// is now nesting-safe: a depth-aware STACK (push on dispatch, POP on SubagentStop)
|
|
5361
5448
|
// closes the contract-escape the flat single-slot model allowed under CC v2.1.172
|
|
@@ -5387,13 +5474,15 @@ function agentHookCommand() {
|
|
|
5387
5474
|
return;
|
|
5388
5475
|
const decision = (0, agent_runtime_js_1.evaluatePreToolUse)(cwd, tool, command);
|
|
5389
5476
|
if (!decision.allow) {
|
|
5477
|
+
// Same root as the decision — see `skillStartCommand` for why the default
|
|
5478
|
+
// is wrong on a hook rail.
|
|
5390
5479
|
(0, observe_js_1.appendObservation)({
|
|
5391
5480
|
kind: "agent",
|
|
5392
5481
|
name: (0, agent_runtime_js_1.readActiveAgent)(cwd) ?? "unknown",
|
|
5393
5482
|
tool,
|
|
5394
5483
|
allowed: false,
|
|
5395
5484
|
reason: decision.message,
|
|
5396
|
-
});
|
|
5485
|
+
}, cwd);
|
|
5397
5486
|
console.error(decision.message);
|
|
5398
5487
|
process.exit(2);
|
|
5399
5488
|
}
|
|
@@ -5438,7 +5527,7 @@ function guardHookCommand() {
|
|
|
5438
5527
|
catch {
|
|
5439
5528
|
/* no stdin */
|
|
5440
5529
|
}
|
|
5441
|
-
const { decision } = (0, guards_js_1.runGuardHook)(
|
|
5530
|
+
const { decision } = (0, guards_js_1.runGuardHook)(runtimeRoot(eventRoot(raw)), raw);
|
|
5442
5531
|
if (!decision.allow) {
|
|
5443
5532
|
console.error(decision.reason ?? "Blocked by a vigiles guard.");
|
|
5444
5533
|
process.exit(2);
|
|
@@ -5450,7 +5539,7 @@ function agentStartCommand(target) {
|
|
|
5450
5539
|
console.error("Usage: vigiles hook-runtime agent-start <agents/<name>.md>");
|
|
5451
5540
|
process.exit(2);
|
|
5452
5541
|
}
|
|
5453
|
-
(0, agent_runtime_js_1.pushActiveAgent)(
|
|
5542
|
+
(0, agent_runtime_js_1.pushActiveAgent)(runtimeRoot(), target);
|
|
5454
5543
|
console.log(`Active agent: ${target}`);
|
|
5455
5544
|
}
|
|
5456
5545
|
/** Dispatch the skill-runtime subcommands. Returns false if unrecognized. */
|
|
@@ -5474,7 +5563,7 @@ async function handleHookRuntime(kind, restArgs) {
|
|
|
5474
5563
|
agentStartCommand(restArgs[0]);
|
|
5475
5564
|
return;
|
|
5476
5565
|
case "agent-done":
|
|
5477
|
-
(0, agent_runtime_js_1.popActiveAgent)(
|
|
5566
|
+
(0, agent_runtime_js_1.popActiveAgent)(runtimeRoot());
|
|
5478
5567
|
return;
|
|
5479
5568
|
case "skill":
|
|
5480
5569
|
skillHookCommand();
|
|
@@ -5486,7 +5575,7 @@ async function handleHookRuntime(kind, restArgs) {
|
|
|
5486
5575
|
skillStartCommand(restArgs[0]);
|
|
5487
5576
|
return;
|
|
5488
5577
|
case "skill-done":
|
|
5489
|
-
(0, skill_runtime_js_1.clearActiveSkill)(
|
|
5578
|
+
(0, skill_runtime_js_1.clearActiveSkill)(runtimeRoot());
|
|
5490
5579
|
return;
|
|
5491
5580
|
case "run-skill":
|
|
5492
5581
|
runSkillCommand(restArgs[0]);
|
|
@@ -5507,11 +5596,11 @@ async function handleHookRuntime(kind, restArgs) {
|
|
|
5507
5596
|
evalLockNudgeHookCommand();
|
|
5508
5597
|
return;
|
|
5509
5598
|
case "effect-enter":
|
|
5510
|
-
(0, effect_region_js_1.setEffectActive)(
|
|
5599
|
+
(0, effect_region_js_1.setEffectActive)(runtimeRoot());
|
|
5511
5600
|
console.log("Effect boundary entered.");
|
|
5512
5601
|
return;
|
|
5513
5602
|
case "effect-exit":
|
|
5514
|
-
(0, effect_region_js_1.clearEffectActive)(
|
|
5603
|
+
(0, effect_region_js_1.clearEffectActive)(runtimeRoot());
|
|
5515
5604
|
return;
|
|
5516
5605
|
default:
|
|
5517
5606
|
console.error(`vigiles hook-runtime: unknown runtime entrypoint "${kind ?? ""}". ` +
|
|
@@ -5542,7 +5631,8 @@ function actionHookCommand() {
|
|
|
5542
5631
|
catch {
|
|
5543
5632
|
/* malformed input → no event, allow */
|
|
5544
5633
|
}
|
|
5545
|
-
const
|
|
5634
|
+
const root = runtimeRoot(eventRoot(raw));
|
|
5635
|
+
const decision = (0, action_gate_js_1.evaluateAction)(event, (0, action_gate_js_1.loadActionGates)(root), root);
|
|
5546
5636
|
if (!decision.allow) {
|
|
5547
5637
|
console.error(decision.message);
|
|
5548
5638
|
process.exit(2);
|
|
@@ -5586,7 +5676,7 @@ function evalLockNudgeHookCommand() {
|
|
|
5586
5676
|
}
|
|
5587
5677
|
if (!file)
|
|
5588
5678
|
return;
|
|
5589
|
-
const cwd =
|
|
5679
|
+
const cwd = runtimeRoot(eventRoot(raw));
|
|
5590
5680
|
const target = (0, node_path_1.relative)(cwd, (0, node_path_1.resolve)(cwd, file)) || file;
|
|
5591
5681
|
// 🔴 THE SAME CONFIG `vigiles lint` READS. This used to pass `basePath` alone,
|
|
5592
5682
|
// so a repo that had switched `untested-skill` off, or pointed `include` at
|
|
@@ -5600,7 +5690,7 @@ function evalLockNudgeHookCommand() {
|
|
|
5600
5690
|
// second gate saying the same thing is a branch no test can distinguish from
|
|
5601
5691
|
// its absence (measured — the mutation passed), i.e. the dead-fragment class
|
|
5602
5692
|
// this same change removed from the runner table.
|
|
5603
|
-
const config = (0, validate_js_1.loadConfig)();
|
|
5693
|
+
const config = (0, validate_js_1.loadConfig)(cwd);
|
|
5604
5694
|
const { options } = untestedRules(config);
|
|
5605
5695
|
// 🔴 THE SAME LAYOUT `vigiles lint` RESOLVES, for the same reason as the config
|
|
5606
5696
|
// above. This used to pass `basePath` alone, so the detector fell back to the
|
|
@@ -5663,10 +5753,12 @@ function refsHookCommand() {
|
|
|
5663
5753
|
}
|
|
5664
5754
|
if (!file || !isInstructionFile(file))
|
|
5665
5755
|
return;
|
|
5666
|
-
|
|
5756
|
+
// Root first: the config read below is anchored on it, and reading the config
|
|
5757
|
+
// from the process's directory is how a disabled rule comes back to life.
|
|
5758
|
+
const cwd = runtimeRoot(eventRoot(raw));
|
|
5759
|
+
const severity = (0, types_js_1.ruleSeverity)((0, validate_js_1.loadConfig)(cwd).rules["unmarked-refs"]);
|
|
5667
5760
|
if (severity === false)
|
|
5668
5761
|
return;
|
|
5669
|
-
const cwd = process.cwd();
|
|
5670
5762
|
const target = (0, node_path_1.relative)(cwd, (0, node_path_1.resolve)(cwd, file)) || file;
|
|
5671
5763
|
let markdown;
|
|
5672
5764
|
try {
|
|
@@ -5749,7 +5841,14 @@ async function installHookFile(file, adapter, registeredProviders = []) {
|
|
|
5749
5841
|
// emitter never produced it. Measured 2026-09-10 in a consumer repo: after a compile,
|
|
5750
5842
|
// one `cd` into a subdirectory made a PreToolUse gate fail to load, and a gate that
|
|
5751
5843
|
// cannot load must block — the repo seized, every command refused including the repair.
|
|
5752
|
-
|
|
5844
|
+
//
|
|
5845
|
+
// 🔴 AND LAUNCHED LOCALLY, NOT THROUGH `npx` — 193 ms against 2545 ms on a warm
|
|
5846
|
+
// cache, thirteen times, on every tool call. The trailing `|| exit N` is what the
|
|
5847
|
+
// shell does when that binary cannot start at all, and N is decided by the hook's
|
|
5848
|
+
// ROLE: see `hookRuntimeRef` and `hookRuntimeMissingExit` for both measurements
|
|
5849
|
+
// and for why a gate and a nudge must answer differently.
|
|
5850
|
+
gateCommand: `${(0, hook_install_js_1.hookRuntimeRef)(adapter.layout.projectRootTokens)} hook-runtime run-program ` +
|
|
5851
|
+
`${(0, hook_install_js_1.hookGateRef)(ref, adapter.layout.projectRootTokens)} || exit ${(0, hook_install_js_1.hookRuntimeMissingExit)((0, hook_program_js_1.dispatchKind)(program))}`,
|
|
5753
5852
|
dialect: adapter.dialect,
|
|
5754
5853
|
hookProtocol: adapter.hookProtocol,
|
|
5755
5854
|
settingsFormat: adapter.layout.settingsFormat,
|
package/dist/core/adapter.d.ts
CHANGED
|
@@ -84,7 +84,28 @@ export interface HarnessAdapter {
|
|
|
84
84
|
* one seam the runner dispatches through). Carried on the bundle so the runner
|
|
85
85
|
* never imports a sibling adapter to find it.
|
|
86
86
|
*/
|
|
87
|
-
|
|
87
|
+
/**
|
|
88
|
+
* 🔴 A THUNK, NOT THE DRIVER, and the indirection is the whole point. A driver
|
|
89
|
+
* lives in `harness-test.ts`, which imports the conformance suite, which
|
|
90
|
+
* imports the compiler, which imports the cross-language symbol index, which
|
|
91
|
+
* loads a NATIVE binary. Holding the driver eagerly meant every consumer of an
|
|
92
|
+
* adapter paid for all of it — including the hook runtime, which reads only
|
|
93
|
+
* `dialect` and `hookProtocol` and never runs a harness test at all.
|
|
94
|
+
*
|
|
95
|
+
* Measured 2026-09-19, `require("./adapter-registry.js")`:
|
|
96
|
+
*
|
|
97
|
+
* eager: 107 modules, 7 ast-grep, 1 native .node
|
|
98
|
+
*
|
|
99
|
+
* ...on a path whose actual work takes about a millisecond. Calling the thunk
|
|
100
|
+
* is what loads the driver, so the test tier pays and the runtime does not.
|
|
101
|
+
*
|
|
102
|
+
* ASYNC because a dynamic `import()` is the only form that defers in BOTH
|
|
103
|
+
* environments this code runs in: the CJS `dist/` build (where TypeScript
|
|
104
|
+
* lowers it to a deferred `require`) and vitest loading the TS sources
|
|
105
|
+
* directly, where a synchronous `require` of a sibling `.ts` does not resolve
|
|
106
|
+
* at all — measured, not assumed.
|
|
107
|
+
*/
|
|
108
|
+
readonly harnessTestDriver?: () => Promise<HarnessTestDriver>;
|
|
88
109
|
/**
|
|
89
110
|
* How strongly a repo at `root` looks like it targets this harness — the CLI
|
|
90
111
|
* uses it to auto-detect which adapter to use (the library selects by import).
|
|
@@ -895,6 +895,49 @@ export interface RawHookEvent {
|
|
|
895
895
|
*/
|
|
896
896
|
readonly cwd?: string;
|
|
897
897
|
}
|
|
898
|
+
/**
|
|
899
|
+
* A hook event whose project root has already been RESOLVED — the shape every
|
|
900
|
+
* consumer past the entry point takes.
|
|
901
|
+
*
|
|
902
|
+
* {@link RawHookEvent} is what arrives on stdin; this is what the runtime works
|
|
903
|
+
* with. The difference is one field, and that field is the whole point: with the
|
|
904
|
+
* root ON the event, an event and a root cannot be handed to different places
|
|
905
|
+
* and disagree. That divergence is not hypothetical. Measured 2026-09-19: the
|
|
906
|
+
* decision layer resolved against the payload while the tamper stamp resolved
|
|
907
|
+
* against `process.cwd()`, and under a git worktree the stamp check did not
|
|
908
|
+
* point at the wrong file — it returned silently and did not run at all.
|
|
909
|
+
*
|
|
910
|
+
* Threading the root as a second parameter beside the event fixes an instance
|
|
911
|
+
* and keeps the shape. A field removes the shape.
|
|
912
|
+
*/
|
|
913
|
+
export interface HookEvent extends RawHookEvent {
|
|
914
|
+
/**
|
|
915
|
+
* The project root, always usable — so no consumer repeats a `?? cwd` fallback
|
|
916
|
+
* and none can forget it.
|
|
917
|
+
*/
|
|
918
|
+
readonly root: string;
|
|
919
|
+
/**
|
|
920
|
+
* Whether {@link root} came from the payload (`$CLAUDE_PROJECT_DIR` or the
|
|
921
|
+
* event's own `cwd`) or is the fallback standing in for a payload that
|
|
922
|
+
* declared none.
|
|
923
|
+
*
|
|
924
|
+
* 🔴 THIS IS THE FIELD A POLICY DECISION READS. What to do with an undeclared
|
|
925
|
+
* root differs by ROLE, not by call site: a gate that cannot locate the
|
|
926
|
+
* project is a gate that cannot decide, and a gate that cannot decide must
|
|
927
|
+
* refuse; a nudge in the same position must stay quiet, because a reminder is
|
|
928
|
+
* never worth a wedged repository. Collapsing both into one behaviour inside
|
|
929
|
+
* the resolver would make that choice unexpressible.
|
|
930
|
+
*/
|
|
931
|
+
readonly rootDeclared: boolean;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Resolve a raw payload's project root ONCE, at the entry point.
|
|
935
|
+
*
|
|
936
|
+
* `fallback` is injected rather than read here, because this module does no IO
|
|
937
|
+
* and holds no ambient state — the caller supplies `process.cwd()`. That is what
|
|
938
|
+
* keeps `process.cwd()` to a single occurrence in the whole hook runtime.
|
|
939
|
+
*/
|
|
940
|
+
export declare function resolveHookEvent(raw: RawHookEvent, env: Readonly<Record<string, string | undefined>>, fallback: string): HookEvent;
|
|
898
941
|
/** The normalized outcome of running a hook program — discriminated by role. */
|
|
899
942
|
export type HookProgramOutcome = {
|
|
900
943
|
readonly kind: "decision";
|
|
@@ -34,6 +34,7 @@ exports.injectionOf = injectionOf;
|
|
|
34
34
|
exports.responseView = responseView;
|
|
35
35
|
exports.experimental_defineReact = experimental_defineReact;
|
|
36
36
|
exports.runReact = runReact;
|
|
37
|
+
exports.resolveHookEvent = resolveHookEvent;
|
|
37
38
|
exports.outcomeWrites = outcomeWrites;
|
|
38
39
|
exports.rememberHookSource = rememberHookSource;
|
|
39
40
|
exports.hookSource = hookSource;
|
|
@@ -717,6 +718,22 @@ function hookRouting(hook) {
|
|
|
717
718
|
// A react MAY also be tool-less (Stop/SessionEnd) — same shape, same reason.
|
|
718
719
|
if (hook.match === undefined)
|
|
719
720
|
return { on: hook.on };
|
|
721
|
+
// 🔴 SAY WHAT IS WRONG, IN THE AUTHOR'S VOCABULARY. From a typed `.ts` hook
|
|
722
|
+
// this is unreachable — tsc rejects a `match` without `tools`. From a `.mjs`
|
|
723
|
+
// hook, which is a supported authoring format, nothing checks it, and
|
|
724
|
+
// reading `.tools.join` off the wrong shape used to surface as
|
|
725
|
+
// `Cannot read properties of undefined (reading 'join')`: a message that
|
|
726
|
+
// names an internal property of an internal function and points nowhere
|
|
727
|
+
// near the author's file. This repo has already paid twice for a diagnosis
|
|
728
|
+
// that sends the reader to the wrong place (the loader that advised
|
|
729
|
+
// `npm run build` when the answer was `npm install`; the bare "cannot be
|
|
730
|
+
// loaded"). A `HookCompileError` is also what the installer catches to
|
|
731
|
+
// print the FILE alongside the reason — a TypeError falls past it.
|
|
732
|
+
if (!Array.isArray(hook.match.tools)) {
|
|
733
|
+
throw new HookCompileError(`a ${hook.role} hook's \`match\` must be \`{ tools: [...] }\` — got ` +
|
|
734
|
+
`${JSON.stringify(hook.match)}. Use \`tools("Edit", "Write")\` to build it; ` +
|
|
735
|
+
`a path condition belongs in the gate's own predicate, not in \`match\`.`);
|
|
736
|
+
}
|
|
720
737
|
return { on: hook.on, matcher: hook.match.tools.join("|") };
|
|
721
738
|
}
|
|
722
739
|
// Bash by construction — see decideProgram; the author no longer declares it.
|
|
@@ -1331,6 +1348,21 @@ function runReact(hook, raw, ctx = {}, root = typeof raw.cwd === "string" ? raw.
|
|
|
1331
1348
|
ctx: ctx,
|
|
1332
1349
|
});
|
|
1333
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Resolve a raw payload's project root ONCE, at the entry point.
|
|
1353
|
+
*
|
|
1354
|
+
* `fallback` is injected rather than read here, because this module does no IO
|
|
1355
|
+
* and holds no ambient state — the caller supplies `process.cwd()`. That is what
|
|
1356
|
+
* keeps `process.cwd()` to a single occurrence in the whole hook runtime.
|
|
1357
|
+
*/
|
|
1358
|
+
function resolveHookEvent(raw, env, fallback) {
|
|
1359
|
+
const declared = projectRootOf(raw, env);
|
|
1360
|
+
return {
|
|
1361
|
+
...raw,
|
|
1362
|
+
root: declared ?? fallback,
|
|
1363
|
+
rootDeclared: declared !== undefined,
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1334
1366
|
/**
|
|
1335
1367
|
* The state writes an outcome declares, filtered to the ones the runtime may
|
|
1336
1368
|
* actually perform. A gate's `Decision` carries none — deliberately: a gate is
|
package/dist/core/linters.js
CHANGED
|
@@ -36,7 +36,6 @@ exports.parseGolangciEnabledLinters = parseGolangciEnabledLinters;
|
|
|
36
36
|
exports.clearCedarCache = clearCedarCache;
|
|
37
37
|
exports.checkLinterRule = checkLinterRule;
|
|
38
38
|
const node_fs_1 = require("node:fs");
|
|
39
|
-
const node_os_1 = require("node:os");
|
|
40
39
|
const node_path_1 = require("node:path");
|
|
41
40
|
const edit_distance_js_1 = require("./edit-distance.js");
|
|
42
41
|
Object.defineProperty(exports, "editDistance", { enumerable: true, get: function () { return edit_distance_js_1.editDistance; } });
|
|
@@ -72,6 +71,7 @@ function augmentToolPath() {
|
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
augmentToolPath();
|
|
74
|
+
const tmp_root_js_1 = require("./tmp-root.js");
|
|
75
75
|
// ---------------------------------------------------------------------------
|
|
76
76
|
// Parsing enforcement references
|
|
77
77
|
// ---------------------------------------------------------------------------
|
|
@@ -267,7 +267,7 @@ function getDetektDefaultRules() {
|
|
|
267
267
|
if (DETEKT_DEFAULT_RULE_CACHE)
|
|
268
268
|
return DETEKT_DEFAULT_RULE_CACHE;
|
|
269
269
|
let rules = new Set();
|
|
270
|
-
const tmp = (0,
|
|
270
|
+
const tmp = (0, tmp_root_js_1.makeTmpDir)("detekt");
|
|
271
271
|
try {
|
|
272
272
|
const target = (0, node_path_1.join)(tmp, "generated-default.yml");
|
|
273
273
|
(0, node_child_process_1.execSync)(`detekt --generate-config --config ${target}`, {
|
|
@@ -481,7 +481,7 @@ function runCheckstyleProbe(configPath, probePath) {
|
|
|
481
481
|
* either placement instantiates.
|
|
482
482
|
*/
|
|
483
483
|
function checkstyleModuleInstantiates(ruleName) {
|
|
484
|
-
const tmp = (0,
|
|
484
|
+
const tmp = (0, tmp_root_js_1.makeTmpDir)("checkstyle");
|
|
485
485
|
try {
|
|
486
486
|
const probe = (0, node_path_1.join)(tmp, "Probe.java");
|
|
487
487
|
(0, node_fs_1.writeFileSync)(probe, "class Probe {}\n");
|