Back to catalogue
WorkflowPermissionDenied
Log auto-mode permission denials
See what auto-mode silently blocked
Appends every auto-mode permission denial to a log file with timestamp, tool name, input and denial reason. Provides visibility into what the auto-mode classifier is blocking, helping you tune allow/deny rules over time.
What does the Log auto-mode permission denials hook do?
Log auto-mode permission denials is a Claude Code PermissionDenied hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. See what auto-mode silently blocked.
Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Understand which tool calls auto-mode is silently blocking in autonomous sessions
- Tune allow/deny rules by reviewing the denial log after a session
- Audit autonomous agent actions for compliance in CI/CD environments
Tags
#auto-mode#permissions#logging#audit#security
settings.json fragment
{
"hooks": {
"PermissionDenied": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/permission-denied-auto-mode-log.mjs",
"type": "command"
}
]
}
]
}
}Script · .claude/hooks/permission-denied-auto-mode-log.mjs
#!/usr/bin/env node
// @hookstack permission-denied-auto-mode-log
// Journalise les permissions refusées (PermissionDenied)
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
export function run(
input,
{
append = appendFileSync,
mkdir = mkdirSync,
projectDir = process.env.CLAUDE_PROJECT_DIR ?? ".",
now = () => new Date().toISOString(),
} = {},
) {
const logPath = join(projectDir, ".claude", "permission-denied.log");
try {
mkdir(dirname(logPath), { recursive: true });
} catch {
/* exists */
}
const line = `${now()} | ${input.tool_name} | ${JSON.stringify(input.tool_input)} | ${input.reason}\n`;
append(logPath, line);
return line;
}
/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
run(input);
}