OpenHands
OpenHands connector wires DefenseClaw into global ~/.openhands/hooks.json command hooks by default, with MCP discovery through ~/.openhands/mcp.json and optional workspace-local skills.
The OpenHands connector wires DefenseClaw into OpenHands lifecycle hooks. DefenseClaw installs the hook globally at ~/.openhands/hooks.json by default so local OpenHands sessions across repositories report into the same DefenseClaw gateway, while OpenHands still talks directly to its configured upstream model provider.
Setup
defenseclaw setup openhands # observe (default) - record only
defenseclaw setup openhands --mode action # block on policy hits via hook deny
defenseclaw setup openhands --workspace /path/to/repoBy default this is global/user-scoped: DefenseClaw writes native OpenHands hook entries into ~/.openhands/hooks.json and installs the shared hook script at ~/.defenseclaw/hooks/openhands-hook.sh. Pass --workspace /path/to/repo only when you intentionally want repo-local .openhands/hooks.json wiring for that repository. There is no proxy-enforcement path for OpenHands — blocking happens hook-side with OpenHands' documented exit code 2 deny response.
The OpenHands connector is hook-only. defenseclaw setup openhands defaults to observability-only mode=observe, and defenseclaw setup openhands --mode action enables hook-native blocking. OpenHands still talks directly to its configured upstream model provider in both modes; DefenseClaw never inserts an LLM proxy.
What setup openhands actually does
The wrapper accepts the same hook-alias flags as the other hook connectors. The underlying guardrail config falls back to the values DefenseClaw ships with — schema-defined in internal/config/config.go and documented on the Defaults page.
| Flag | Default | What it does |
|---|---|---|
--yes / -y | off | Skip the confirmation prompt. |
--mode observe|action | observe | observe records only; action returns OpenHands' documented deny response on policy hits. |
--restart / --no-restart | --restart | Bounce defenseclaw-gateway after applying changes so the new hooks wire in. |
--with-local-stack / --no-local-stack | --no-local-stack | Also bring up the bundled Prom/Loki/Tempo/Grafana stack via setup local-observability up. |
--workspace / --workspace-dir | unset | Opt into repo-local .openhands/hooks.json; unset means global ~/.openhands/hooks.json. |
setup openhands is shorthand for setup guardrail --connector openhands: it adds or reconfigures OpenHands, defaults the connector to observe mode, and can join an existing hook-connector roster when you choose Add. claw.workspace_dir is cleared for global setup and set only when --workspace is supplied. guardrail.mode changes to action only when --mode action is passed. To tune OpenHands after install, keep using defenseclaw setup guardrail --connector openhands.
Version contract
DefenseClaw validates the installed OpenHands CLI version during setup when the trusted openhands --version probe succeeds. The OpenHands connector is documented and tested against OpenHands CLI 1.16.0; the hook contract remains listed as "unversioned / documented hooks" until upstream publishes a hook-version floor for .openhands/hooks.json.
Global OpenHands setup observes local OpenHands sessions that load ~/.openhands/hooks.json. A repository-local .openhands/hooks.json can still override or layer with that behavior depending on OpenHands' own config loading. Use --workspace when you want DefenseClaw to manage a repo-local file explicitly.
Hook schema
DefenseClaw writes the native OpenHands shape: top-level snake_case event keys, each containing matcher groups and command hooks. It does not wrap the config in a Claude Code-style top-level hooks object.
{
"pre_tool_use": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "~/.defenseclaw/hooks/openhands-hook.sh",
"timeout": 60
}
]
}
]
}OpenHands can block pre_tool_use, user_prompt_submit, and stop. Non-blocking events still feed telemetry and audit. When DefenseClaw blocks, the hook script returns JSON shaped like OpenHands expects:
{ "decision": "deny", "reason": "policy denied" }OpenHands documents additionalContext for context injection. DefenseClaw uses that field for downgraded alerts or confirm verdicts because OpenHands does not expose a native ask/approval surface in the documented hook contract.
Local surfaces
OpenHands MCP servers are managed in ~/.openhands/mcp.json, so MCP discovery is global by default. Current OpenHands skills use .agents/skills; global setup resolves that as ~/.agents/skills, and workspace setup resolves it under the pinned repository. DefenseClaw also discovers deprecated .openhands/skills and .openhands/microagents paths, installed skills in ~/.openhands/skills/installed, and the public OpenHands extensions cache at ~/.openhands/cache/skills/public-skills/skills. That cache is where OpenHands' own "Loaded: N skills" count comes from after the SDK syncs public skills. OpenHands has no plugin install surface in the documented local contract, so DefenseClaw treats plugins as unsupported and hides the Plugins TUI panel while claw.mode=openhands.
claw.workspace_dir is optional. Empty means global user scope. When set via --workspace, workspace-local skills/rules discovery uses that repository until you clear it with a global setup run or point it at another workspace.
The resolved paths are captured in ~/.defenseclaw/hook_contract_lock.json under locations, so defenseclaw doctor can report both the hook contract and the concrete hook/MCP/skill locations it is using.
Hook capabilities
Block events
- pre_tool_use
- user_prompt_submit
- stop
Native ask events
None — confirm verdicts are downgraded with the raw action preserved.
OpenHands can block supported hook events but has no native human-approval surface. Confirm verdicts become an additionalContext alert with raw_action: "confirm" preserved; the TUI and audit log are review-only.
Disable
defenseclaw setup guardrail --disableTeardown removes DefenseClaw-owned hook entries from ~/.openhands/hooks.json or the pinned workspace hook file and leaves unrelated OpenHands hooks untouched.
GitHub Copilot CLI
Copilot CLI connector wires global ~/.copilot/hooks by default, with optional workspace .github/hooks. Native ask supported on preToolUse; block events cover permissionRequest, agentStop, subagentStop, postToolUseFailure.
Antigravity
Antigravity (`agy`) connector registers all five 2.0 lifecycle hooks in ~/.gemini/config/hooks.json; the empirically verified PreToolUse event provides native ask/deny decisions that override --dangerously-skip-permissions.