@spardutti/claude-skills 2.11.0 → 2.11.1
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/bin/cli.mjs +6 -4
- package/lib/setup-hook.mjs +21 -29
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -158,10 +158,12 @@ async function main() {
|
|
|
158
158
|
await setupClaudeMd();
|
|
159
159
|
|
|
160
160
|
// Report what the gauntlet will actually run, so a silent no-op is visible.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
// This asks the installed hook itself — never a second copy of its logic.
|
|
162
|
+
const gates = await detectStack(CWD);
|
|
163
|
+
if (gates) {
|
|
164
|
+
for (const gate of gates.split(";").map((g) => g.trim()).filter(Boolean)) {
|
|
165
|
+
console.log(` ${chalk.dim(`Gauntlet gate: ${gate}`)}`);
|
|
166
|
+
}
|
|
165
167
|
} else {
|
|
166
168
|
console.log(` ${chalk.yellow("!")} ${chalk.dim("Gauntlet found no test runner here — it will stay asleep.")}`);
|
|
167
169
|
console.log();
|
package/lib/setup-hook.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile, readFile, chmod, unlink } from "node:fs/promises";
|
|
2
2
|
import { join, resolve, dirname } from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
3
4
|
|
|
4
5
|
// PreToolUse gate on Write|Edit|MultiEdit. Blocks the tool call unless
|
|
5
6
|
// a per-session marker file exists at /tmp/claude-skill-gate-<SESSION_ID>.
|
|
@@ -469,37 +470,28 @@ const GAUNTLET_FILENAME = "gauntlet.sh";
|
|
|
469
470
|
const LEGACY_EVAL_FILENAME = "skill-forced-eval-hook.sh";
|
|
470
471
|
const LEGACY_AUDIT_RUNNER_FILENAME = "skill-audit-runner.sh";
|
|
471
472
|
|
|
472
|
-
//
|
|
473
|
-
//
|
|
473
|
+
// Asks the installed hook what it would run, rather than reimplementing its
|
|
474
|
+
// detection here. A second copy drifts: the JS one reported "no test runner"
|
|
475
|
+
// for repos the shell script had already learned to handle.
|
|
474
476
|
export async function detectStack(targetDir = process.cwd()) {
|
|
475
477
|
const dir = resolve(targetDir);
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (/"typecheck"\s*:/.test(pkg)) typecheck = "npm run typecheck";
|
|
494
|
-
else if (await has("tsconfig.json")) typecheck = "tsc";
|
|
495
|
-
}
|
|
496
|
-
if (!test && (await has("pyproject.toml") || await has("pytest.ini") || await has("setup.cfg"))) {
|
|
497
|
-
test = "pytest";
|
|
498
|
-
if (await has("mypy.ini") || (await read("pyproject.toml")).includes("tool.mypy")) {
|
|
499
|
-
typecheck = "mypy";
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return { test, typecheck };
|
|
478
|
+
const hook = join(dir, ".claude", "hooks", GAUNTLET_FILENAME);
|
|
479
|
+
return new Promise((done) => {
|
|
480
|
+
const child = spawn("bash", [hook], {
|
|
481
|
+
cwd: dir,
|
|
482
|
+
env: { ...process.env, CLAUDE_PROJECT_DIR: dir, GAUNTLET_DEBUG: "1", GAUNTLET_DRYRUN: "1" },
|
|
483
|
+
});
|
|
484
|
+
let err = "";
|
|
485
|
+
child.stderr.on("data", (c) => { err += c; });
|
|
486
|
+
child.on("error", () => done(null));
|
|
487
|
+
child.on("close", () => {
|
|
488
|
+
const line = err.split("\n").find((l) => l.startsWith("gauntlet: would run:"));
|
|
489
|
+
if (!line) return done(null);
|
|
490
|
+
const gates = line.slice("gauntlet: would run:".length).trim();
|
|
491
|
+
done(gates && !gates.startsWith("nothing") ? gates : null);
|
|
492
|
+
});
|
|
493
|
+
child.stdin.end('{"session_id":"install"}');
|
|
494
|
+
});
|
|
503
495
|
}
|
|
504
496
|
|
|
505
497
|
// Written only when detection finds nothing — otherwise the hook auto-detects and
|