1
0
Fork 0

feat: initial companion repo for OpenClaw vs Claude Code article

40 files demonstrating every major OpenClaw capability using Claude Code:
- 3 agents (researcher, writer, reviewer)
- 3 skills (daily-briefing, slack-message, web-research)
- 2 security hooks (pre-tool-use blocker, post-tool-use logger)
- 10 self-contained examples with copy-paste prompts
- Complete feature map (20 capabilities, 11 full match, 7 different, 2 gap)
- Security docs including NemoClaw comparison
- Automation, messaging, browser, memory documentation

Zero dependencies. Clone and run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-03-26 09:47:29 +01:00
commit 2491f5c732
40 changed files with 2037 additions and 0 deletions

75
hooks/README.md Normal file
View file

@ -0,0 +1,75 @@
# Hooks
Claude Code hooks are shell scripts or HTTP endpoints that run
before or after tool execution. They are the primary security
mechanism for Claude Code, equivalent to OpenClaw's exec approvals
and Docker sandboxing.
## Files in this directory
| File | Hook event | Purpose |
|------|-----------|---------|
| `pre-tool-use.sh` | PreToolUse | Blocks dangerous shell commands |
| `post-tool-use.sh` | PostToolUse | Logs all tool executions |
| `audit.log` | (generated) | Append-only audit trail |
## How hooks work
Hooks are configured in `.claude/settings.json`:
```json
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "bash hooks/pre-tool-use.sh"
}]
}]
}
}
```
### PreToolUse
Runs before a tool executes. The script receives JSON on stdin
with `tool_name` and `tool_input`. Return decisions:
- Exit 0: allow (no output needed)
- Exit 2 + JSON `{"decision": "block", "reason": "..."}`: block the tool call
### PostToolUse
Runs after a tool executes. Same stdin format plus `tool_output`.
Cannot block (already executed). Use for logging, notifications,
or triggering follow-up actions.
### Other hook events
Claude Code supports these additional events:
| Event | When it fires |
|-------|--------------|
| SessionStart | When Claude Code launches |
| SessionEnd | When the session closes |
| Stop | When Claude Code finishes a response |
| SubagentStop | When a subagent completes |
| UserPromptSubmit | Before processing user input |
| PreCompact | Before context compaction |
| Notification | When Claude Code shows a notification |
## Comparison to OpenClaw
| Feature | OpenClaw | Claude Code |
|---------|----------|-------------|
| Exec blocking | /approve command + DM pairing | PreToolUse hooks |
| Audit logging | command-logger hook | PostToolUse hooks |
| Tool deny lists | Per-session/agent config | disallowedTools + settings.json deny |
| Container isolation | Docker sandbox (off/non-main/all) | macOS sandbox-exec |
| Policy engine | NemoClaw YAML policies | Hook scripts (any logic) |
| HTTP webhooks | Built-in webhook system | HTTP hooks (POST JSON to URL) |
Claude Code hooks are more flexible (arbitrary shell logic) but
require more setup. OpenClaw's approach is more structured but
less customizable.

21
hooks/post-tool-use.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
# PostToolUse hook: Log all tool executions to an audit trail.
#
# This hook runs AFTER every tool execution and appends a log entry
# to hooks/audit.log. Useful for reviewing what Claude Code did
# during a session.
#
# OpenClaw equivalent: command-logger hook + OpenTelemetry (NemoClaw)
# Claude Code approach: PostToolUse hooks with custom logging
input=$(cat)
tool_name=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null)
tool_input=$(echo "$input" | python3 -c "import sys,json; d=json.load(sys.stdin).get('tool_input',{}); print(d.get('command', d.get('file_path', d.get('pattern', str(d)[:100])))" 2>/dev/null)
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
log_dir="$(dirname "$0")"
log_file="$log_dir/audit.log"
echo "$timestamp | $tool_name | $tool_input" >> "$log_file"
exit 0

53
hooks/pre-tool-use.sh Executable file
View file

@ -0,0 +1,53 @@
#!/bin/bash
# PreToolUse hook: Block dangerous shell commands before execution.
#
# This hook reads the tool input from stdin (JSON with tool_name and tool_input)
# and blocks commands that could cause serious damage.
#
# How it works:
# - Claude Code calls this script BEFORE executing any Bash command
# - If the script exits with code 2, the command is BLOCKED
# - The "decision" field in stdout JSON controls the outcome
#
# OpenClaw equivalent: exec approvals + tool deny lists + Docker sandbox
# Claude Code approach: hook-based guardrails (more flexible, user-controlled)
input=$(cat)
tool_name=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null)
command=$(echo "$input" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null)
# Only check Bash commands
if [ "$tool_name" != "Bash" ]; then
exit 0
fi
# Blocked patterns
blocked_patterns=(
"rm -rf /"
"rm -rf ~"
"rm -rf \$HOME"
"mkfs"
"dd if="
":(){:|:&};:"
"chmod -R 777 /"
"curl.*|.*bash"
"wget.*|.*bash"
"curl.*|.*sh"
"wget.*|.*sh"
"> /dev/sda"
"sudo rm"
"shutdown"
"reboot"
"init 0"
"init 6"
)
for pattern in "${blocked_patterns[@]}"; do
if echo "$command" | grep -qi "$pattern"; then
echo '{"decision": "block", "reason": "Blocked by security hook: command matches dangerous pattern '"'$pattern'"'"}'
exit 2
fi
done
# Allow everything else
exit 0