Hooks now accept an optional priority field and HookRegistry returns hooks in stable highest-priority-first order.\n\nVerified with:\n- uv run pytest -o addopts='' tests/test_hooks tests/test_plugins/test_lifecycle_flow.py tests/test_plugins/test_loader.py tests/test_ui/test_runtime_plugin_tools.py -q\n- uv run ruff check src/openharness/hooks tests/test_hooks tests/test_plugins/test_lifecycle_flow.py tests/test_plugins/test_loader.py tests/test_ui/test_runtime_plugin_tools.py
_inject_arguments replaced $ARGUMENTS with json.dumps(payload) directly
into shell command strings. JSON encoding wraps values in double quotes,
but bash still expands $(...), backticks, and other metacharacters inside
double-quoted strings. A payload containing {"command": "$(whoami)"}
would result in the subshell being executed.
Add shlex.quote() around the serialized JSON when substituting into
command hooks (shell_escape=True), wrapping it in single quotes so bash
treats the entire payload as a literal string. Prompt hooks continue to
use raw substitution since they pass the result to the LLM API, not a
shell.
The existing OPENHARNESS_HOOK_PAYLOAD environment variable (already set
on line 83) remains the recommended way to access payload data from hook
scripts, since env vars are not subject to shell expansion.
A lightweight open-source Python implementation of the Agent Harness architecture.
44x lighter than Claude Code (11K vs 512K lines), 98% core tool coverage.
- 43 tools with Pydantic validation and parallel execution
- Skills system compatible with anthropics/skills (17+ tested)
- Plugin system compatible with claude-code/plugins (12+ tested)
- API retry with exponential backoff
- Multi-level permissions with path rules
- React/Ink TUI with "Oh my Harness!" branding
- 114 unit tests + 6 E2E test suites
- MIT License