mirror of
https://github.com/Xevion/dotfiles.git
synced 2026-01-31 00:24:06 -06:00
102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
#!/usr/bin/env bun
|
|
/**
|
|
* Chezmoi Safety Hook - Prevents unsafe chezmoi commands in Claude Code
|
|
*
|
|
* Blocks:
|
|
* - Commands with --force/-f flags
|
|
* - Commands without specific file arguments (chezmoi apply with no files)
|
|
*/
|
|
|
|
interface ToolInput {
|
|
command?: string;
|
|
description?: string;
|
|
}
|
|
|
|
interface HookInput {
|
|
tool_name: string;
|
|
tool_input: ToolInput;
|
|
}
|
|
|
|
async function readStdin(): Promise<string> {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of Bun.stdin.stream()) {
|
|
chunks.push(Buffer.from(chunk));
|
|
}
|
|
return Buffer.concat(chunks).toString("utf-8");
|
|
}
|
|
|
|
async function main() {
|
|
let inputData: HookInput;
|
|
|
|
try {
|
|
const input = await readStdin();
|
|
inputData = JSON.parse(input);
|
|
} catch {
|
|
// Invalid JSON, allow command to proceed
|
|
process.exit(0);
|
|
}
|
|
|
|
const toolName = inputData.tool_name ?? "";
|
|
const toolInput = inputData.tool_input ?? {};
|
|
const command = toolInput.command ?? "";
|
|
|
|
// Only validate Bash commands
|
|
if (toolName !== "Bash") {
|
|
process.exit(0);
|
|
}
|
|
|
|
// Only validate chezmoi commands
|
|
if (!/\bchezmoi\b/.test(command)) {
|
|
process.exit(0);
|
|
}
|
|
|
|
const errors: string[] = [];
|
|
|
|
// Check for force flags
|
|
if (/\b(?:--force|-f)\b/.test(command)) {
|
|
errors.push(
|
|
"Force flag detected: chezmoi commands with --force or -f are not allowed"
|
|
);
|
|
errors.push(
|
|
" Reason: Force flag bypasses safety checks and can overwrite changes"
|
|
);
|
|
}
|
|
|
|
// Check for apply/update without specific files
|
|
if (/\bchezmoi\s+(?:apply|update)\b/.test(command)) {
|
|
const match = command.match(/\bchezmoi\s+(?:apply|update)\s+(.*)/);
|
|
if (match) {
|
|
let args = match[1].trim();
|
|
// Remove known flags to see if file arguments remain
|
|
args = args.replace(/(?:--[\w-]+|-[a-z])\b/g, "").trim();
|
|
|
|
if (!args) {
|
|
errors.push(
|
|
"No specific files specified: 'chezmoi apply' must target specific files"
|
|
);
|
|
errors.push(" Allowed: chezmoi apply ~/.bashrc");
|
|
errors.push(
|
|
" Allowed: chezmoi apply --dry-run ~/.config/fish/config.fish"
|
|
);
|
|
errors.push(" Blocked: chezmoi apply");
|
|
errors.push(" Blocked: chezmoi apply --dry-run");
|
|
errors.push("");
|
|
errors.push(
|
|
" Use 'chezmoi diff' to preview changes before asking me to apply"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If errors found, block the command
|
|
if (errors.length > 0) {
|
|
process.stderr.write(errors.join("\n") + "\n");
|
|
process.exit(2); // Exit code 2 blocks the command
|
|
}
|
|
|
|
// No issues, allow command
|
|
process.exit(0);
|
|
}
|
|
|
|
main();
|