@kaynooo/guard-tools 0.3.0 → 0.4.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/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/cli.js +82 -16
- package/package.json +6 -6
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Kaynooo <kaynooo
|
|
3
|
+
Copyright (c) 2026 Kaynooo <contact@kaynooo.fr>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ guard check --json --cwd "$PWD" - < command.txt
|
|
|
13
13
|
| Rule | Refuses | Lives in |
|
|
14
14
|
| --- | --- | --- |
|
|
15
15
|
| `remote` | `ssh`, `scp`, `sftp`, `mosh`, `rsync` to a host, `DOCKER_HOST=ssh://`, `docker -H ssh://` | `src/remote.ts` |
|
|
16
|
-
| `git` | `git stash` (pointing at `base`), `git add -A`/`.`/`-u`, `git commit -a` in any flag cluster, `git push` the user did not ask for, `git push --tags` without `--atomic` | `src/git.ts` |
|
|
16
|
+
| `git` | `git stash` (pointing at `proof`, `base`, a commit first or `git worktree add`), `git add -A`/`.`/`-u`, `git commit -a` in any flag cluster, `git push` the user did not ask for, `git push --tags` without `--atomic` | `src/git.ts` |
|
|
17
17
|
| `commit` | a commit message breaking the format: no Conventional Commit title, a trailer, a body over two lines, a bullet list, a hard wrap; `-m`, `-F <file>` and `-F -` with a heredoc are read; the refusal spells the rule out and points at `gc` | `src/commit.ts`, the validator in `@kaynooo/git-tools` |
|
|
18
18
|
| `sql` | a SQL client, console or dump tool fed a write (INSERT, UPDATE, DELETE, DDL, migrations, fixtures) outside a test database | `@kaynooo/db-tools` (`src/clients.ts`, `src/guard.ts`) |
|
|
19
19
|
| `env` | a command reading, writing or printing an env file or the whole environment; `guard path` refuses a file tool opening one | `@kaynooo/env-tools` (`src/guard.ts`) |
|
package/dist/cli.js
CHANGED
|
@@ -340,13 +340,34 @@ class Completing {
|
|
|
340
340
|
var completion = { active: false };
|
|
341
341
|
var OPTIONAL = /\[[^\]]+\]/g;
|
|
342
342
|
var USAGE_PREFIX = /^usage:\s*/i;
|
|
343
|
+
var FULL_STOP = /\.$/;
|
|
343
344
|
function usageLine(usage, colors = paint(process3.stdout)) {
|
|
344
345
|
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
345
346
|
}
|
|
346
|
-
function helpText(usage, options, colors = paint(process3.stdout), examples = []) {
|
|
347
|
-
return [usageLine(usage, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
|
|
347
|
+
function helpText(usage, options, colors = paint(process3.stdout), { examples = [], replaces } = {}) {
|
|
348
|
+
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
|
|
348
349
|
`);
|
|
349
350
|
}
|
|
351
|
+
function replacesRows(replaces, colors = paint(process3.stdout)) {
|
|
352
|
+
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
353
|
+
}
|
|
354
|
+
function wantsHelpJson(env = process3.env) {
|
|
355
|
+
return env.KIT_HELP === "json";
|
|
356
|
+
}
|
|
357
|
+
function description(usage, { examples = [], replaces } = {}) {
|
|
358
|
+
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
359
|
+
`);
|
|
360
|
+
const first = lines[0];
|
|
361
|
+
const colon = first.indexOf(": ");
|
|
362
|
+
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
363
|
+
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
364
|
+
return {
|
|
365
|
+
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
366
|
+
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
367
|
+
...replaces === undefined ? {} : { replaces },
|
|
368
|
+
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
|
|
369
|
+
};
|
|
370
|
+
}
|
|
350
371
|
function exampleRows(examples, colors = paint(process3.stdout)) {
|
|
351
372
|
if (examples.length === 0)
|
|
352
373
|
return [];
|
|
@@ -389,14 +410,14 @@ function parse(args, usage, options, settings = {}) {
|
|
|
389
410
|
const rest = separator === -1 ? undefined : args.slice(separator + 1);
|
|
390
411
|
let parsed;
|
|
391
412
|
try {
|
|
392
|
-
parsed = parseArgs({ args: own, options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
413
|
+
parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
393
414
|
} catch (error) {
|
|
394
415
|
badUsage(parseError(error));
|
|
395
416
|
}
|
|
396
417
|
const values = parsed.values;
|
|
397
418
|
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
398
419
|
if (values.help === true) {
|
|
399
|
-
console.log(helpText(usage, all, undefined, settings
|
|
420
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, all, undefined, settings));
|
|
400
421
|
throw new HelpShown;
|
|
401
422
|
}
|
|
402
423
|
if (settings.positionals !== undefined)
|
|
@@ -411,6 +432,25 @@ function checkPositionals(given, names) {
|
|
|
411
432
|
if (!variadic && given.length > names.length)
|
|
412
433
|
badUsage(`unexpected argument ${given[names.length]}`);
|
|
413
434
|
}
|
|
435
|
+
function joinDashValues(args, options) {
|
|
436
|
+
const longOf = new Map(Object.entries(options).flatMap(([name, option]) => option.short === undefined ? [[`--${name}`, name]] : [[`--${name}`, name], [`-${option.short}`, name]]));
|
|
437
|
+
const isOption = (arg) => arg === "--" || longOf.has(arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg);
|
|
438
|
+
const joined = [];
|
|
439
|
+
for (let i = 0;i < args.length; i++) {
|
|
440
|
+
const arg = args[i] ?? "";
|
|
441
|
+
if (arg === "--")
|
|
442
|
+
return [...joined, ...args.slice(i)];
|
|
443
|
+
const name = longOf.get(arg);
|
|
444
|
+
const next = args[i + 1];
|
|
445
|
+
if (name !== undefined && options[name]?.type === "string" && next !== undefined && next.startsWith("-") && next !== "-" && !isOption(next)) {
|
|
446
|
+
joined.push(`--${name}=${next}`);
|
|
447
|
+
i++;
|
|
448
|
+
} else {
|
|
449
|
+
joined.push(arg);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return joined;
|
|
453
|
+
}
|
|
414
454
|
function parseError(error) {
|
|
415
455
|
const message = upTo(error instanceof Error ? error.message : String(error), ". ");
|
|
416
456
|
return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
|
|
@@ -481,9 +521,11 @@ function closest(name, names) {
|
|
|
481
521
|
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
482
522
|
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
483
523
|
}
|
|
484
|
-
async function help({ path, header, commands, defaultCommand, examples }, stream) {
|
|
524
|
+
async function help({ path, header, summary, commands, defaultCommand, examples, replaces }, stream) {
|
|
485
525
|
const colors = paint(stream);
|
|
486
526
|
const prefix = path.join(" ");
|
|
527
|
+
if (wantsHelpJson())
|
|
528
|
+
return JSON.stringify(description(`${prefix} <command> [options]: ${summary}`, { examples, replaces }));
|
|
487
529
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
488
530
|
const options = fallback !== undefined && "run" in fallback ? await optionsOf(fallback.run) : undefined;
|
|
489
531
|
const own = Object.entries(options ?? {}).filter(([name]) => !(name in TOP_OPTIONS));
|
|
@@ -491,6 +533,7 @@ async function help({ path, header, commands, defaultCommand, examples }, stream
|
|
|
491
533
|
header(colors),
|
|
492
534
|
"",
|
|
493
535
|
usageLine(`${prefix} ${defaultCommand === undefined ? colors.cyan("<command>") : `[${colors.cyan("<command>")}]`} [options]`, colors),
|
|
536
|
+
...replacesRows(replaces, colors),
|
|
494
537
|
"",
|
|
495
538
|
...commandRows(commands, colors),
|
|
496
539
|
...own.length === 0 ? [] : ["", `${colors.bold(`Options of ${defaultCommand}:`)} ${colors.dim("the command run when none is named")}`, ...optionRows(Object.fromEntries(own), colors)],
|
|
@@ -511,13 +554,21 @@ function printError(prefix, message, hint, cause) {
|
|
|
511
554
|
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
512
555
|
console.error(`${head} ${message}${under("cause", cause, colors.dim)}${under("hint", hint, colors.yellow)}`);
|
|
513
556
|
}
|
|
514
|
-
function
|
|
557
|
+
function printJsonError(message, hint, cause) {
|
|
558
|
+
console.error(JSON.stringify({ error: message, ...hint === undefined ? {} : { hint }, ...cause === undefined ? {} : { cause } }));
|
|
559
|
+
}
|
|
560
|
+
function wantsJson(argv) {
|
|
561
|
+
const end = argv.indexOf("--");
|
|
562
|
+
return (end === -1 ? argv : argv.slice(0, end)).includes("--json");
|
|
563
|
+
}
|
|
564
|
+
function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
515
565
|
if (error instanceof HelpShown)
|
|
516
566
|
return 0;
|
|
517
567
|
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
518
568
|
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
519
569
|
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
520
|
-
|
|
570
|
+
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
571
|
+
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
521
572
|
return error instanceof Exit ? error.code : 1;
|
|
522
573
|
}
|
|
523
574
|
async function dispatch(level, typed, { version, redact }) {
|
|
@@ -554,12 +605,12 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
554
605
|
}
|
|
555
606
|
if ("commands" in command) {
|
|
556
607
|
const { summary } = command;
|
|
557
|
-
return dispatch({ path: [...level.path, name], header: (colors) => `${colors.bold(`${prefix} ${name}`)} ${summary}`, commands: command.commands, aliases: command.aliases, defaultCommand: undefined, examples: command.examples }, rest, { version, redact });
|
|
608
|
+
return dispatch({ path: [...level.path, name], header: (colors) => `${colors.bold(`${prefix} ${name}`)} ${summary}`, summary, commands: command.commands, aliases: command.aliases, defaultCommand: undefined, examples: command.examples, replaces: undefined }, rest, { version, redact });
|
|
558
609
|
}
|
|
559
610
|
try {
|
|
560
611
|
return await command.run(rest);
|
|
561
612
|
} catch (error) {
|
|
562
|
-
return reportThrown(`${prefix} ${name}`, error, redact);
|
|
613
|
+
return reportThrown(`${prefix} ${name}`, error, redact, wantsJson(rest));
|
|
563
614
|
}
|
|
564
615
|
}
|
|
565
616
|
var COMPLETE = "__complete";
|
|
@@ -600,7 +651,7 @@ async function printCandidates(found) {
|
|
|
600
651
|
async function runCli(cli, argv = process4.argv.slice(2)) {
|
|
601
652
|
if (argv[0] === COMPLETE)
|
|
602
653
|
return printCandidates(candidates(cli.commands, cli.aliases, cli.defaultCommand, argv.slice(1)));
|
|
603
|
-
return dispatch({ path: [cli.name], header: (colors) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${cli.about}`, commands: cli.commands, aliases: cli.aliases, defaultCommand: cli.defaultCommand, examples: cli.examples }, argv, { version: cli.version, redact: cli.redact });
|
|
654
|
+
return dispatch({ path: [cli.name], header: (colors) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${cli.about}`, summary: cli.about, commands: cli.commands, aliases: cli.aliases, defaultCommand: cli.defaultCommand, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
|
|
604
655
|
}
|
|
605
656
|
// ../kit/src/process.ts
|
|
606
657
|
import { constants } from "os";
|
|
@@ -624,7 +675,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore" } = {}) {
|
|
|
624
675
|
return { code: exitCodeOf(proc.exitCode, proc.signalCode), chunks, output, text: output.toString() };
|
|
625
676
|
}
|
|
626
677
|
// package.json
|
|
627
|
-
var version = "0.
|
|
678
|
+
var version = "0.4.0";
|
|
628
679
|
|
|
629
680
|
// ../kit/src/shell.ts
|
|
630
681
|
var SEPARATORS = new Set([`
|
|
@@ -1523,7 +1574,7 @@ var SHORT_CLUSTER = /^-[A-Z]+$/i;
|
|
|
1523
1574
|
var STAGES_ALL = /[Au]/;
|
|
1524
1575
|
var ALL_TAGS = new Set(["--tags", "--follow-tags"]);
|
|
1525
1576
|
var STAGE_BY_PATH = `Stage by path: the tree carries unrelated work and peer sessions edit it live. ${USE_GC}`;
|
|
1526
|
-
var STASH = "Never `git stash`: a pop with nothing saved applies someone else's old stash. To
|
|
1577
|
+
var STASH = "Never `git stash`: a pop with nothing saved applies someone else's old stash. To prove a test fails without your change, `proof --keep <test> -- <cmd>` runs it at HEAD in a temporary worktree and again here; `base -- <cmd>` runs any command at HEAD (`--ref <rev>` another commit), the working tree untouched. To set edits aside before a cherry-pick, a rebase or a checkout, commit them with `gc` first, or do the other work in `git worktree add`.";
|
|
1527
1578
|
var ADD_ALL = `\`git add\` over the whole tree is refused. ${STAGE_BY_PATH}`;
|
|
1528
1579
|
var COMMIT_ALL = `\`git commit -a\` stages the whole tree and is refused. ${STAGE_BY_PATH}`;
|
|
1529
1580
|
var PUSH = "`git push` runs only when the user asked for a push in their latest prompt: say the commit is ready and let them ask.";
|
|
@@ -2092,18 +2143,25 @@ function checkPath(path) {
|
|
|
2092
2143
|
var BINS = {
|
|
2093
2144
|
check: "`check`: runs the repo's check, typecheck, lint and test steps and prints only what failed, each full log saved to a file; validate a change with it rather than chaining the scripts (`--only lint,test`, `--list`).",
|
|
2094
2145
|
fix: "`fix`: runs the repo's fixers and formatters (its fix, lint:fix and format scripts, just fmt, go fmt, cargo fmt, ruff) and lists the files they changed.",
|
|
2146
|
+
errs: "`errs [log]`, `errs -- <cmd>`: turns tsc, tsgo, eslint, bun check and bun test output into one `file:line:col: message (rule)` line per error, counting the lines it did not recognise (`--json`); use it instead of reading raw type, lint or test logs.",
|
|
2095
2147
|
db: '`db "<sql>"`: one read-only statement against the Postgres in DATABASE_URL (`-e KEY` for another) or, without one, the compose project\'s running Postgres service (`-s <service>`, `-d <database>`), credentials never shown, a write printed for the user to run; `db tables`, `db describe <table>`, `db dbs`.',
|
|
2096
2148
|
ev: "`ev`: .env files read and edited with values masked (`ev list`, `ev get KEY`, `ev grep <regex>`, `ev show <rev>:<file>`, `ev set`, `ev diff`, `ev missing`); `ev audit [dir]` finds env files others can read or git tracks, and compose files writing secrets out.",
|
|
2097
2149
|
redact: "`redact`: masks secrets in piped output, `<cmd> | redact`.",
|
|
2098
2150
|
pkg: "`pkg <name>[@version]`: checks an npm package before it is added: whether the name exists or is a near miss of a popular one, the newest version at least two weeks old, licence, install scripts; `bun add`, `npm install`, `pnpm add` and `yarn add` run it first and are refused on what it flags. `pkg bump <@scope/lib> [version]` waits until the registry serves a release, then `bun add --exact`s it in every consumer under ~/Workspace (`--dry-run`), `pkg consumers <name>` lists them; use it instead of a `bun info` and sleep loop and a `bun add` per repo.",
|
|
2099
2151
|
stack: "`stack port <name>`: the port a service name always hashes to, for every host port a compose file publishes; `stack audit [dir]` cross-checks release.config.ts against its compose files.",
|
|
2100
|
-
ft: "`ft`: Forgejo CI runs, logs and waits, releases, issue checklists and claims, in a repo whose origin is on Forgejo.",
|
|
2152
|
+
ft: "`ft`: Forgejo CI runs, logs and waits, releases, issue checklists and claims, milestones (`ft milestones`, `ft milestone <m>`, `ft new -m`), in a repo whose origin is on Forgejo.",
|
|
2101
2153
|
sub: "`sub --from <text> --to <text> <file>...`: replaces text, writing nothing unless each match count is as expected (1, `-n N`, `--all`), `--regex`, `--pairs <json|->` for several in order, `--dry-run --diff`; use it instead of sed -i, a bun -e or python editor.",
|
|
2102
2154
|
base: "`base [--ref <rev>] [--keep <path>...] -- <cmd>`: runs a command at HEAD (or a ref) in a temporary worktree, removed afterwards, ignored env files and node_modules/vendor linked; use it instead of git stash to see whether a test fails without your change (`--keep` the new test).",
|
|
2155
|
+
proof: "`proof [--ref <rev>] [--keep <path>...] -- <cmd>`: runs the command at HEAD (or a ref) in a temporary worktree, then in the working tree, and exits 0 only when it fails before and passes now, printing the failing side's output; use it to prove a new test fails without your change (`--keep` the test).",
|
|
2103
2156
|
gst: "`gst [--log N]`: branch, upstream, changed counts and HEAD in one line, then the changed paths by staged, unstaged and untracked; use it instead of `git status --short` and `git log --oneline`.",
|
|
2104
2157
|
gc: "`gc <title> [-m <line>]... [--ticket <line>]... -- <path>...`: commits exactly those paths, refusing with nothing staged when the message breaks a commit rule, a path has no change or other files are already staged; use it instead of chaining git add, status and commit.",
|
|
2105
2158
|
waitfor: "`waitfor --file <log> --until <regex> [--fail <regex>]`, `--port <n>`, `--url <url>`, `--pid <n>` (`-t 5m`): waits until the line shows, the port listens, the URL answers 2xx or the process exits, exits 1 on a --fail line with the lines around it, 2 on a timeout with the last lines; `waitfor task <id>` waits for a background Bash task and prints its exit code, last lines and failure lines; use it instead of sleep and tail, `until ! pgrep -f` or polling loops.",
|
|
2106
2159
|
gl: "`gl ci [ref]` waits for the GitLab pipeline covering a commit and prints each failed job's tests (exit 0/1/2), `gl runs`, `gl logs <pipeline> [-j job] [--full]`; use it instead of `glab ci` or `glab api .../pipelines/<id>/jobs` polled in a sleep loop.",
|
|
2160
|
+
peek: "`peek <file:from-to>...` prints several spans with line numbers in one call, `peek --def <symbol>` a whole declaration, `--refs <symbol>` its usages by file, `--outline <file>` the declarations and members, `--around <regex> -C 3` the lines around matches; use it instead of `sed -n N,Mp`, `grep -n` chains and whole-file reads.",
|
|
2161
|
+
"todo-scan": "`todo-scan [path...]` lists the TODO, FIXME and owner/repo#N comments as `<file>:<line>: <comment>`, `--stale` only those whose issue is closed (exit 1), through ft; use it to find the workarounds a shipped fix lets you drop.",
|
|
2162
|
+
doctor: "`doctor`: checks this host's agent-tools install, one line per check with its fix: each bin's version against the registry, tea's login, the Claude Code hooks, the Bun floor (`--json`); run it when a bin lacks a command or a flag its docs name.",
|
|
2163
|
+
"agent-tools": "`agent-tools list`: every agent-tools bin in one line, what it does and what it is reached for instead of, read from each bin's own --help (`--json` adds the synopsis and examples); run it to find the bin that fits a task.",
|
|
2164
|
+
hotspots: "`hotspots [dir] [--since <date>] [-n 20]`: ranks the source files by complexity times git churn (commits touching each in the window, 6 months by default), tests and generated files left out, `--json`; use it to pick which complex files are worth splitting first.",
|
|
2107
2165
|
out: "`out -- <cmd>`: folds long output, keeping the errors and saving the full log; run noisy builds, installs, tests and linters through it."
|
|
2108
2166
|
};
|
|
2109
2167
|
function binsSection(installed) {
|
|
@@ -2180,7 +2238,9 @@ var checkUsage = `guard check [options] [<command> | -]
|
|
|
2180
2238
|
|
|
2181
2239
|
Checks a shell command against the rules: remote machines, git (stash, whole-tree add and commit, push, tags), commit messages, SQL writes, env files, package managers and in-place edits (sed -i and replace scripts, pointed at sub).
|
|
2182
2240
|
Prints each broken rule with the route to take instead and exits 1, prints nothing and exits 0 when the command is clean.
|
|
2183
|
-
The command is read from stdin with - or when none is given; a rule may rewrite it instead (the pinned package manager, a noisy run through out), printed as \`runs as: <command
|
|
2241
|
+
The command is read from stdin with - or when none is given; a rule may rewrite it instead (the pinned package manager, a noisy run through out), printed as \`runs as: <command>\`.
|
|
2242
|
+
|
|
2243
|
+
Exits 0 when the command is clean, 1 when a rule is broken, 2 on a usage error.`;
|
|
2184
2244
|
var checkOptions = {
|
|
2185
2245
|
json: jsonOption("{violations, command, notes, packages} for a hook"),
|
|
2186
2246
|
cwd: { type: "string", description: "the directory the command runs in (default: here)" },
|
|
@@ -2189,7 +2249,9 @@ var checkOptions = {
|
|
|
2189
2249
|
};
|
|
2190
2250
|
var pathUsage = `guard path [--json] <path>...
|
|
2191
2251
|
|
|
2192
|
-
Checks the paths a file tool would read, edit or write: env files are refused
|
|
2252
|
+
Checks the paths a file tool would read, edit or write: env files are refused.
|
|
2253
|
+
|
|
2254
|
+
Exits 0 when every path is clean, 1 when one is refused, 2 on a usage error.`;
|
|
2193
2255
|
var pathOptions = {
|
|
2194
2256
|
json: jsonOption("{violations} for a hook")
|
|
2195
2257
|
};
|
|
@@ -2197,7 +2259,9 @@ var hookUsage = `guard hook <${EVENTS.join("|")}>
|
|
|
2197
2259
|
|
|
2198
2260
|
Answers a Claude Code hook: reads the event JSON on stdin and prints the reply JSON, nothing when the call goes ahead untouched.
|
|
2199
2261
|
pre-tool-use refuses a Bash call breaking a rule, an install pkg refuses, and a Read, Edit or Write of an env file; user-prompt-submit remembers whether the prompt asked for a push; session-start lists the installed agent-tools bins.
|
|
2200
|
-
A failure exits 1, which Claude Code reports without blocking the call
|
|
2262
|
+
A failure exits 1, which Claude Code reports without blocking the call.
|
|
2263
|
+
|
|
2264
|
+
Exits 0 when it answered, 1 on a failure or an unknown event (never 2, which would block the call).`;
|
|
2201
2265
|
function readText(path) {
|
|
2202
2266
|
try {
|
|
2203
2267
|
return readFileSync(path, "utf8");
|
|
@@ -2283,6 +2347,8 @@ process6.exitCode = await runCli({
|
|
|
2283
2347
|
name: "guard",
|
|
2284
2348
|
version,
|
|
2285
2349
|
about: "the safety rules a Claude Code Bash call is held to, each refusal naming the route",
|
|
2350
|
+
replaces: "a hook script per rule, each wording its own refusal",
|
|
2351
|
+
examples: [['guard check "git push --force"', "the rules a command breaks, with their routes"], ["guard path .env", "whether a file tool may open a path"]],
|
|
2286
2352
|
commands: {
|
|
2287
2353
|
check: { run: check, summary: "check a shell command, printing each broken rule with its route" },
|
|
2288
2354
|
path: { run: async (args) => Promise.resolve(path(args)), summary: "check a path a file tool would open" },
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaynooo/guard-tools",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "guard: the safety rules a Claude Code Bash call is held to (git, remote machines, SQL writes, env files, package managers, commit messages), each refusal naming the route.",
|
|
6
|
-
"author": "Kaynooo <kaynooo
|
|
6
|
+
"author": "Kaynooo <contact@kaynooo.fr>",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"homepage": "https://git.
|
|
8
|
+
"homepage": "https://git.kaynooo.fr/kaynooo/agent-tools/src/branch/main/packages/guard#readme",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://git.
|
|
11
|
+
"url": "git+https://git.kaynooo.fr/kaynooo/agent-tools.git",
|
|
12
12
|
"directory": "packages/guard"
|
|
13
13
|
},
|
|
14
|
-
"bugs": "https://git.
|
|
14
|
+
"bugs": "https://git.kaynooo.fr/kaynooo/agent-tools/issues",
|
|
15
15
|
"keywords": [
|
|
16
16
|
"claude-code",
|
|
17
17
|
"cli",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"bun": ">=1.4.3"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
|
-
"registry": "https://git.
|
|
32
|
+
"registry": "https://git.kaynooo.fr/api/packages/kaynooo/npm/"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "$npm_execpath build src/cli.ts --target bun --outdir dist",
|