Modular validation system for Claude Code hooks. Provides security command validation (PreToolUse), TypeScript type checking (PostToolUse), and lint checking (PostToolUse). Includes configurable security patterns, multi-linter detection, webhook notifications, and structured logging. Use when setting up or troubleshooting Claude Code validation hooks.
Resources
1Install
npx skillscat add leobrival/topographic-plugins-official/validation Install via the SkillsCat registry.
Validation System
Modular validation system for Claude Code with security, linting, and type checking hooks.
Overview
This skill provides three hook entry points for Claude Code:
| Hook | Type | Script | Purpose |
|---|---|---|---|
validate-command |
PreToolUse | dist/commands/validate-command.js |
Block dangerous bash commands |
type-check |
PostToolUse | dist/commands/type-check.js |
TypeScript type validation on file edits |
lint-check |
PostToolUse | dist/commands/lint-check.js |
Lint validation on file edits |
Architecture
scripts/validation/
├── src/
│ ├── commands/ # Hook entry points
│ │ ├── validate-command.ts # PreToolUse: security validation
│ │ ├── type-check.ts # PostToolUse: TypeScript checking
│ │ ├── lint-check.ts # PostToolUse: lint checking
│ │ └── test-webhook.ts # Webhook testing utility
│ └── lib/
│ ├── config.ts # Configuration manager (env var interpolation)
│ ├── types.ts # Core type definitions
│ ├── formatters.ts # Output formatting utilities
│ ├── logger.ts # Structured logger with file output
│ ├── detectors/
│ │ ├── project-detector.ts # Detect project root, tsconfig, package.json
│ │ ├── package-detector.ts # Detect package manager (bun > pnpm > yarn > npm)
│ │ └── linter-detector.ts # Detect available linters (Biome, ESLint, Prettier, etc.)
│ ├── executors/
│ │ ├── command-executor.ts # Shell command execution with fallbacks
│ │ └── linter-executor.ts # Linter execution with priority order
│ ├── validators/
│ │ ├── command-validator.ts # Security pattern matching
│ │ ├── type-validator.ts # TypeScript type checking
│ │ └── lint-validator.ts # Lint file validation
│ └── webhooks/
│ ├── ifttt-client.ts # IFTTT webhook client
│ ├── webhook-manager.ts # Multi-endpoint sender with retry
│ ├── webhook-service.ts # High-level webhook API
│ └── notification-templates.ts # Template selection and rendering
├── config/
│ ├── default.json # Main configuration
│ ├── security/
│ │ ├── dangerous-patterns.json # Blocked command patterns
│ │ └── safe-patterns.json # Allowed command patterns
│ └── webhooks/
│ ├── endpoints.json # Webhook endpoint configuration
│ └── notification-templates.json # Notification templates
├── dist/ # Compiled output (generated by `tsc`)
├── package.json
├── tsconfig.json
└── biome.jsonInstallation
Step 1: Build the project
After plugin install/update, build the validation project:
cd ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation
bun install && bun run buildStep 2: Configure hooks in settings.json
Add or update the following hooks in ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bun ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/validate-command.js"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "bun ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/type-check.js"
},
{
"type": "command",
"command": "bun ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/lint-check.js"
},
{
"type": "command",
"command": "bun run format || true"
}
]
}
]
}
}Step 3: Verify installation
Test that the hooks work correctly:
# Check scripts exist
ls ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/
# Test validate-command (should exit 0 for safe commands)
echo '{"tool_name":"Bash","tool_input":{"command":"ls -la"}}' | bun ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/validate-command.js
# Test validate-command (should exit 2 for dangerous commands)
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' | bun ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/validate-command.jsSecurity Patterns
Dangerous Patterns (Blocked)
Commands matching these patterns are blocked with exit code 2:
rm -rf- Recursive force remove (critical)sudo/su- Elevated privileges (high)chmod 777- Unsafe permissions (high)eval()/exec()- Code execution (high)curl | sh/wget | sh- Remote code execution (critical)/etc/passwd//etc/shadow- System file access (high/critical)../- Path traversal (medium)dd if=/mkfs./fdisk- Disk operations (critical)
Safe Patterns (Allowed)
Commands matching these patterns are explicitly allowed:
git,gh- Version controlbun,npm,pnpm,yarn- Package managersdocker,docker-compose- Containersvercel,railway,neon,convex- Cloud platformsls,cd,pwd,cat,echo- Basic shellmkdir,touch,cp,mv,grep- File operationscurl(without pipes),wget(without pipes) - HTTP requests
Customizing Patterns
Edit the JSON files in config/security/:
dangerous-patterns.json- Add patterns to blocksafe-patterns.json- Add patterns to allow
Each pattern has: pattern (regex), description, optional severity (critical/high/medium/low).
Linter Detection
The system auto-detects available linters in the current project:
| Linter | Detection Method |
|---|---|
| Biome | biome.json, biome.jsonc, or @biomejs/biome in package.json |
| ESLint | .eslintrc.*, eslint.config.*, or eslint in package.json |
| Prettier | .prettierrc*, prettier.config.*, or prettier in package.json |
| markdownlint | .markdownlint.*, .markdownlintrc, or markdownlint-cli* in package.json |
| TSLint | tslint.json (legacy) |
Priority order: Biome > ESLint > Prettier > markdownlint > TSLint
Webhook Notifications
Supports IFTTT, Slack, and Discord webhook endpoints for real-time notifications.
Events
command_blocked/command_allowed- Security eventslint_error/lint_success- Linting eventstypecheck_error/typecheck_success- Type checking eventssecurity_alert- Security alertssession_start/session_end- Session lifecycle
Configuration
Edit config/webhooks/endpoints.json to configure webhook endpoints.
Uses ${ENV_VAR} interpolation for secrets.
Testing Webhooks
cd ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation
bun run test:webhookLogging
Structured logging to ~/.claude/logs/validation/ with daily rotation.
Control via environment variables:
LOG_DIR- Custom log directoryLOG_LEVEL- Log level (debug/info/warn/error)
Development
Build
cd skills/validation/scripts/validation
bun install
bun run build # Compile TypeScript
bun run build:watch # Watch modeLint
bun run lint # Check
bun run lint:fix # Auto-fix
bun run format # FormatTroubleshooting
Hook not running
- Verify dist/ exists:
ls ~/.claude/plugins/marketplaces/topographic-plugins-official/plugins/dev/skills/validation/scripts/validation/dist/commands/ - If missing, rebuild:
cd <path> && bun install && bun run build - Check settings.json hook paths match the marketplace path
False positives from type-check
The type-check hook may report failures with errorCount: 0 — this is a known false positive. The hook returns non-zero but the actual error count is zero. Safe to ignore.
Blocked command that should be allowed
Add a new pattern to config/security/safe-patterns.json:
{
"pattern": "^your-command\\s+",
"description": "Your safe command"
}Logs not appearing
Check that ~/.claude/logs/validation/ exists and is writable.
Override with: LOG_DIR=/custom/path bun <script>