Share hooks—not just skills—across Codex and Claude Code.
agent-harness is a Nix-native composition layer for hooks, skills, agent instructions, command
permissions, and provider settings. Define one environment, then render the native files expected
by both Codex and Claude Code.
The packaged default stays deliberately small. Personal or team behavior lives in a complete source tree beside the rest of its configuration and can compose local or Nix-built skills and hook bundles without forking this repository.
AGENTS.md and command permissions
Local or command-generated skills
Local or command-generated hook bundles
Provider settings
|
v
agent-harness
|-- Claude Code: settings, hooks, and skills
`-- Codex: config, hooks, adapters, rules, and skills
- Shared hooks: reuse hook behavior across Codex and Claude Code, including tools whose CLI normally writes directly into each provider's config directory.
- One set of guardrails: generate Claude Bash permissions and Codex execpolicy from one neutral
allow/ask/denysource. - Composable extensions: combine built-in behavior with external skills and hook bundles.
- CLI-to-Nix bridge: turn command output into a skill, or capture supported files created by hook installers inside an isolated build home.
- Provider-native output: render the adapters, hook wiring, permissions, rules, and configuration expected by each agent.
- State-aware Codex updates: replace harness-owned keys while preserving tool-owned and user-owned Codex configuration.
The built-in minimal source contains a neutral AGENTS.md, empty skill, hook, and permission
sets, and no model defaults. It exists as a usable starting point and as the CLI default. It does
not contain the repository author's personal hooks or skills.
A customized environment is a complete flat source tree passed with --source or configured as
the Home Manager module's source. Its manifest.json declares required runtime commands, and
verify reports any that are missing. This keeps personal policy changes in the repository where
they are owned while agent-harness remains a reusable renderer and deployment boundary.
The Home Manager module accepts ordinary paths and derivations. A personal setup can therefore live next to the rest of a user's Nix configuration:
dotfiles/
└── agents/
├── manifest.json
├── AGENTS.md
├── command_permissions.json
├── hooks.json
├── skill_rendering.json
├── hooks/
├── skills/
├── claude/
└── codex/
programs.agent-harness={enable=true;source=./agents;skills={team-workflows=./agent-sources/skills/team-workflows;};hooks={notifications=./agent-sources/hook-bundles/notifications;};};The flake also exposes builders for integrations owned by other CLIs. Herdr
for example, prints a release-matched skill with herdr --skill and installs provider hooks with
herdr integration install:
letharnessLib=agent-harness.lib.${pkgs.system};herdrSkill=harnessLib.buildSkillFromCommand{name="herdr";command=["${herdr}/bin/herdr""--skill"];};herdrHooks=harnessLib.buildHookBundleFromCommands{name="herdr";commands=[["${herdr}/bin/herdr""integration""install""claude"]["${herdr}/bin/herdr""integration""install""codex"]];};in{programs.agent-harness={enable=true;skills.herdr=herdrSkill;hooks.herdr=herdrHooks;};}buildSkillFromCommand writes the command's standard output to SKILL.md and rejects empty output.
buildHookBundleFromCommands runs commands in order with an isolated HOME, captures supported
Claude and Codex artifacts, and writes a versioned bundle. Each command is an argument list: the
first item is the executable and the remaining items are passed as arguments without a shell.
Hook installer executables must be absolute; absolute Nix package paths are recommended for skill
commands as well. Builds should be deterministic and must not depend on interactive input or
network access.
The hook builder captures .claude/settings.json, .claude/hooks/, .codex/hooks.json,
.codex/hooks/, .codex/config.toml, and top-level .codex/*.sh files. If generated hook commands
embed a build-time path that must differ at runtime, pass
commandReplacements = [{ from = "..."; to = "..."; }].
For installers such as moshi-hook install, use the same hook
builder. Pairing credentials and daemon state are runtime concerns and are not captured in the
bundle:
moshiHooks=agent-harness.lib.${pkgs.system}.buildHookBundleFromCommands{name="moshi";commands=[["${moshiHook}/bin/moshi-hook""install"]];};Release binaries are currently built for x86_64-unknown-linux-musl. The quickest install on that
platform uses the shell installer generated by cargo-dist:
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/furedea/agent-harness/releases/latest/download/agent-harness-installer.sh \
| shThen install or verify the managed harness files:
agent-harness install --prefix "$HOME"
agent-harness verify --prefix "$HOME"This installs the neutral built-in minimal source. Pass --source <path> to render a complete
personal or team source tree instead.
For a source-based Cargo install:
cargo install --locked --git https://github.com/furedea/agent-harness agent-harnessThe shell and Cargo installers place the binary under Cargo's bin directory, normally
$HOME/.cargo/bin. Make sure that directory is on PATH.
For Nix on Apple Silicon macOS:
nix profile install github:furedea/agent-harnessAfter installing the binary, agent-harness install is still required to render the Codex and
Claude Code configuration. verify checks the required installed paths and the runtime commands
declared by the resolved source manifest.
Use this on x86_64 Linux when you want the binary release but do not want to pipe an installer
into Bash. The current cargo-dist configuration uses its default Unix archive format, .tar.xz.
mkdir -p "$HOME/.local/agent-harness""$HOME/.local/bin"
curl -fsSLO \
https://github.com/furedea/agent-harness/releases/latest/download/agent-harness-x86_64-unknown-linux-musl.tar.xz
tar -xJf agent-harness-x86_64-unknown-linux-musl.tar.xz \
-C "$HOME/.local/agent-harness" \
--strip-components=1
ln -sf "$HOME/.local/agent-harness/agent-harness""$HOME/.local/bin/agent-harness"
agent-harness install --prefix "$HOME"
agent-harness verify --prefix "$HOME"Use this when you already have a Rust toolchain and want to build from the repository.
cargo install --locked --git https://github.com/furedea/agent-harness agent-harness
agent-harness install --prefix "$HOME"
agent-harness verify --prefix "$HOME"The flake currently exposes a package only for aarch64-darwin.
nix run github:furedea/agent-harness -- install --prefix "$HOME"
nix run github:furedea/agent-harness -- verify --prefix "$HOME"Use the Home Manager module when your agent config is managed by Nix. The module is exposed as
homeManagerModules.default and currently targets aarch64-darwin.
{inputs={nixpkgs.url="github:NixOS/nixpkgs/nixpkgs-25.11-darwin";home-manager={url="github:nix-community/home-manager";inputs.nixpkgs.follows="nixpkgs";};agent-harness.url="github:furedea/agent-harness";};outputs={agent-harness,home-manager,nixpkgs,
...
}:
{homeConfigurations.example=home-manager.lib.homeManagerConfiguration{pkgs=importnixpkgs{system="aarch64-darwin";};modules=[agent-harness.homeManagerModules.default{programs.agent-harness.enable=true;}];};};}As shown in Compose with Nix, additional skill directories and external hook
bundles can be composed into both providers without copying them into this repository. Skill
directories must contain SKILL.md. Installed names may contain only lowercase ASCII letters,
digits, and hyphens, and may not shadow a built-in skill. Regular files are copied verbatim;
symlinks are ignored.
Each bundle must contain hook_bundle.json with {"version":1} and may provide Claude settings,
Codex hooks, Codex feature flags, and provider hook scripts. Scripts are installed below
~/.claude/hooks/external/<name>/ or ~/.codex/hooks/external/<name>/. Hook JSON is merged
structurally; external Codex configuration may add only [features] entries.
The complete module interface is:
| Option | Default | Meaning |
|---|---|---|
enable | false | Enable the package and managed files. |
package | flake package | Select the agent-harness executable. |
source | this flake | Profile collection or complete flat profile source tree. |
profile | "minimal" | Select the packaged composition base. |
agentsMd | profile file | Replace the shared AGENTS.md / CLAUDE.md source. |
commandPermissions | profile file | Replace provider-neutral allow / ask / deny rules. |
skills | {} | Add skill directories or derivations by installed name. |
hooks | {} | Add versioned hook bundle directories or derivations by name. |
claude.enable | true | Install Claude Code files. |
claude.settings | {} | Optionally override keys in the selected Claude base. |
codex.enable | true | Install Codex files. |
codex.settings | {} | Optionally override keys in the selected Codex base. |
Generated hook wiring, Bash command permissions, and protected paths remain harness-managed after
the provider settings merge. This prevents a settings overlay from silently removing enforcement.
Set either provider's enable option to false to install only the other provider.
The Home Manager module materializes ~/.claude/settings.json as a writable regular file because
Claude Code may update it. On activation, generated top-level keys replace the corresponding
existing values, while top-level keys absent from the generated settings are preserved. Other
Claude Code files remain Home Manager-managed symlinks.
Cargo-dist publishes a .sha256 file for each release archive.
curl -fsSLO \
https://github.com/furedea/agent-harness/releases/latest/download/agent-harness-x86_64-unknown-linux-musl.tar.xz
curl -fsSLO \
https://github.com/furedea/agent-harness/releases/latest/download/agent-harness-x86_64-unknown-linux-musl.tar.xz.sha256
sha256sum -c agent-harness-x86_64-unknown-linux-musl.tar.xz.sha256agent-harness install --prefix "$HOME" writes the rendered harness into Codex and Claude Code
config directories.
| Path | Purpose |
|---|---|
~/.codex/AGENTS.md | Codex agent instructions |
~/.codex/config.toml | Managed Codex config |
~/.codex/hooks.json | Codex hook wiring |
~/.codex/hooks/ | Codex hook adapters |
~/.codex/rules/default.rules | Codex command permissions |
~/.codex/skills/ | Rendered Codex skills |
~/.claude/CLAUDE.md | Claude Code agent instructions |
~/.claude/settings.json | Claude Code settings |
~/.claude/hooks/ | Claude Code hooks and policy guards |
~/.claude/skills/ | Rendered Claude Code skills |
~/.claude/statusline/ | Claude Code status line command |
Installation replaces the managed hook and skill directories and rewrites Claude Code's managed settings. Codex config synchronization replaces only these managed top-level keys and preserves other Codex-owned or user-owned state such as project trust and marketplace data:
model, model_reasoning_effort, personality, approval_policy, sandbox_mode,
approvals_reviewer, notice, tui, plugins, features, default_permissions,
permissions
verify checks the required paths and runtime commands declared by the resolved source manifest. It
does not compare installed file contents with the selected source.
Most users only need:
agent-harness install --prefix "$HOME"
agent-harness verify --prefix "$HOME"For a project-local installation, set both the output prefix and runtime root. --prefix controls
where files are written; --runtime-root controls where generated hook commands and protection
rules expect those files at runtime. The runtime root must be absolute.
agent-harness install \
--source /absolute/path/to/agents \
--prefix "$PWD" \
--runtime-root "$PWD"
agent-harness verify --source /absolute/path/to/agents --prefix "$PWD"The default runtime root remains the user's home directory, so existing user-level installs do not
need the additional option. Relocated hook commands expose the selected directory as
AGENT_HARNESS_ROOT; source-owned adapters that invoke sibling harness assets should resolve them
from ${AGENT_HARNESS_ROOT:-$HOME}.
Inspect the built-in components managed by the resolved harness source:
agent-harness list
agent-harness list skills
agent-harness list hooks
agent-harness list skills --source ./agents
agent-harness list hooks --provider codex --source ./agentsThe CLI also exposes lower-level generation commands for inspecting or composing individual outputs:
| Command | Output |
|---|---|
generate-claude-settings | Complete Claude Code settings JSON |
generate-claude-hooks | Claude Code hook JSON |
generate-codex-config-source | Complete managed Codex config source |
generate-codex-config-fragment | Guarded-filesystem TOML fragment |
generate-codex-hooks | Codex hook JSON |
generate-codex-rules | Codex execpolicy rules |
generate-command-permissions | Shared runtime command permissions |
generate-forbidden-commands | Global precise forbidden-command regex rules |
generate-hook-bundle | Isolated, versioned external hook bundle |
generate-skills | Provider-specific built-in and external skill tree |
sync-claude-settings | Top-level merge into existing Claude settings |
sync-codex-config | Managed-key merge into an existing Codex config |
agent-harness generate-skills \
--provider codex \
--extra-skill external-tool=/path/to/external-tool \
--output "$HOME/.codex/skills"
agent-harness generate-skills \
--provider claude \
--output "$HOME/.claude/skills"
agent-harness generate-hook-bundle \
--spec /path/to/hook-bundle-spec.json \
--output /path/to/hook-bundle
agent-harness generate-codex-hooks \
--output "$HOME/.codex/hooks.json"
agent-harness generate-codex-rules \
--output "$HOME/.codex/rules/default.rules"
agent-harness generate-command-permissions \
--output "$HOME/.claude/hooks/rules/command_permissions.json"
agent-harness generate-forbidden-commands \
--output "$HOME/.claude/hooks/rules/forbidden_commands.json"
agent-harness generate-codex-config-source \
--output /tmp/codex-config-source.toml
agent-harness sync-codex-config \
--source /tmp/codex-config-source.toml \
--target "$HOME/.codex/config.toml"
agent-harness generate-claude-settings \
--output "$HOME/.claude/settings.json"A hook bundle spec lists absolute installer executables and their arguments. Installers run in an isolated temporary home, and only supported Claude and Codex hook artifacts are captured. Optional command replacements let a build-time executable path become a stable runtime path:
{
"version": 1,
"installers": [
{
"executable": "/nix/store/example/bin/example-hook",
"arguments": ["install", "--target", "claude,codex"]
}
],
"command_replacements": [
{
"from": "/nix/store/example/bin/example-hook",
"to": "/opt/homebrew/bin/example-hook"
}
]
}For Nix-managed customization, prefer a complete flat source tree or the direct Home Manager
options above. Render a custom tree with
agent-harness install --source <path> --prefix "$HOME".
| Goal | Edit |
|---|---|
| Declare runtime command requirements | agents/manifest.json |
| Change shared agent instructions | agents/AGENTS.md |
| Add or edit a skill | agents/skills/<skill>/SKILL.md |
| Change provider-specific skill metadata | agents/skill_rendering.json |
| Change shared command permissions | agents/command_permissions.json |
| Change precise global command forms | agents/hooks/rules/{allowed,forbidden}_commands.json |
| Add or change hook wiring | agents/hooks.json |
| Add or change Claude hooks | agents/hooks/*.sh |
| Add or change Codex hook adapters | agents/codex/hooks/*.sh |
| Change Codex base config | agents/codex/config.toml |
| Change Claude base settings | agents/claude/settings.base.json |
Command permissions have two layers. command_permissions.json at the profile root is the
provider-neutral source of shared token prefixes. Each rule has a decision (allow, ask, or
deny), a prefix, and a required justification:
{
"version": 1,
"rules": [
{
"decision": "ask",
"prefix": ["git", "push"],
"justification": "Publishing changes requires user confirmation."
}
]
}The generator maps these rules to Claude Code's allow, ask, and deny Bash permissions and to
Codex's allow, prompt, and forbidden execpolicy decisions. The JSON files under hooks/rules/
contain POSIX extended regular expressions for precise global command forms.
A repository may add precise rules without changing agent-harness by creating either of these optional files:
<git-root>/.agents/hooks/rules/allowed_commands.json
<git-root>/.agents/hooks/rules/forbidden_commands.json
Both files use this schema:
{
"version": 1,
"rules": [
{
"patterns": ["^uv run --frozen example$"],
"justification": "Allow the repository's validated example task."
}
]
}Project allow rules only approve precise forms within an allow prefix already declared in the
active profile's command_permissions.json; they cannot introduce a new shared prefix.
Project forbidden rules may reject any matching command segment. A forbidden match takes
precedence over an allow match, and an invalid project rule file fails closed.
Use the installed binary with a local source tree:
agent-harness install --source "$HOME/dotfiles/agents" --prefix "$HOME"For repeated local rendering:
export AGENT_HARNESS_SOURCE="$HOME/dotfiles/agents"
agent-harness install --prefix "$HOME"When --source is omitted, agent-harness resolves assets in this order:
- explicit
--source AGENT_HARNESS_SOURCEshare/agent-harnessbelow the binary directoryshare/agent-harnessbelow the binary installation prefix- current directory when it is an
agent-harnesssource tree - embedded packaged assets
After resolving the source root, the CLI selects profiles/<profile> when present. Otherwise, it
treats the root as a complete flat profile containing manifest.json, AGENTS.md, hooks/,
skills/, claude/, and codex/. This lets a dotfiles repository expose agents/ directly while
the same binary continues to work from release tarballs, Nix builds, Cargo installs, and local
checkouts.
See CONTRIBUTING.md for the development environment, local checkout commands, quality gates, and release process.