Skip to content

Repository files navigation

agent-harness

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

Why

  • 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 / deny source.
  • 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.

Sources

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.

Compose with Nix

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"]];};

Quick 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 \
| sh

Then 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-harness

The 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-harness

After 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.

Other Installation Methods

Release Archive

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"

Cargo

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"

Nix

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"

Home Manager

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:

OptionDefaultMeaning
enablefalseEnable the package and managed files.
packageflake packageSelect the agent-harness executable.
sourcethis flakeProfile collection or complete flat profile source tree.
profile"minimal"Select the packaged composition base.
agentsMdprofile fileReplace the shared AGENTS.md / CLAUDE.md source.
commandPermissionsprofile fileReplace 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.enabletrueInstall Claude Code files.
claude.settings{}Optionally override keys in the selected Claude base.
codex.enabletrueInstall 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.

Optional Integrity Check

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.sha256

What Gets Installed

agent-harness install --prefix "$HOME" writes the rendered harness into Codex and Claude Code config directories.

PathPurpose
~/.codex/AGENTS.mdCodex agent instructions
~/.codex/config.tomlManaged Codex config
~/.codex/hooks.jsonCodex hook wiring
~/.codex/hooks/Codex hook adapters
~/.codex/rules/default.rulesCodex command permissions
~/.codex/skills/Rendered Codex skills
~/.claude/CLAUDE.mdClaude Code agent instructions
~/.claude/settings.jsonClaude 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.

Usage

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 ./agents

The CLI also exposes lower-level generation commands for inspecting or composing individual outputs:

CommandOutput
generate-claude-settingsComplete Claude Code settings JSON
generate-claude-hooksClaude Code hook JSON
generate-codex-config-sourceComplete managed Codex config source
generate-codex-config-fragmentGuarded-filesystem TOML fragment
generate-codex-hooksCodex hook JSON
generate-codex-rulesCodex execpolicy rules
generate-command-permissionsShared runtime command permissions
generate-forbidden-commandsGlobal precise forbidden-command regex rules
generate-hook-bundleIsolated, versioned external hook bundle
generate-skillsProvider-specific built-in and external skill tree
sync-claude-settingsTop-level merge into existing Claude settings
sync-codex-configManaged-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"
}
]
}

Customization

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".

GoalEdit
Declare runtime command requirementsagents/manifest.json
Change shared agent instructionsagents/AGENTS.md
Add or edit a skillagents/skills/<skill>/SKILL.md
Change provider-specific skill metadataagents/skill_rendering.json
Change shared command permissionsagents/command_permissions.json
Change precise global command formsagents/hooks/rules/{allowed,forbidden}_commands.json
Add or change hook wiringagents/hooks.json
Add or change Claude hooksagents/hooks/*.sh
Add or change Codex hook adaptersagents/codex/hooks/*.sh
Change Codex base configagents/codex/config.toml
Change Claude base settingsagents/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"

Source Resolution

When --source is omitted, agent-harness resolves assets in this order:

  1. explicit --source
  2. AGENT_HARNESS_SOURCE
  3. share/agent-harness below the binary directory
  4. share/agent-harness below the binary installation prefix
  5. current directory when it is an agent-harness source tree
  6. 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.

Contributing

See CONTRIBUTING.md for the development environment, local checkout commands, quality gates, and release process.

About

A Nix-native, composable agent harness for Codex and Claude Code hooks, skills, and safety policies.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

Generated from furedea/template-rust