build_context_table runs `git rev-parse` + `git status --porcelain`, which was called on every iteration of the parts loop. In huge repos like Chromium each call costs ~3s, so the 8-script loop took ~24s when cwd was heavy. The static fields (stdin, env, cwd, git) don't vary across scripts, so hoist the call to before the loop. Cuts git subprocess invocations from 2*N to 2, total wall time from ~86ms to ~20ms in this repo, and from ~24s to ~3s in Chromium. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| scripts | ||
| src | ||
| .envrc | ||
| .gitignore | ||
| AGENTS.md | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| config.demo.yaml | ||
| deepseek-pricing.json | ||
| default.nix | ||
| flake.lock | ||
| flake.nix | ||
| nvchecker.toml | ||
| README.md | ||
| shell.nix | ||
agent-run
agent-run is a small launcher for coding agents.
It keeps provider settings in one place, then adapts them for each target agent instead of asking you to manually rewrite per-agent config files every time.
Current focus:
claudecodexhermescrush
Current protocol support:
anthropicopenai-responsesopenai-chat-completions
Why
Different coding agents expect different configuration shapes:
- some read environment variables
- some want a config file or profile
- some mix both
agent-run gives you one provider config and adapts it to the target agent at launch time.
The provider config stays generic; each agent adapter is responsible for mapping that generic config into the shape the downstream tool expects.
What It Does
- Centralizes provider definitions in one config file
- Resolves secrets from either
keyorkey_command - Optionally loads model lists from provider APIs and caches normalized results on disk
- Validates protocol compatibility before launch
- Negotiates the final protocol for agents that support more than one wire API
- Generates temporary runtime config where needed
- Forwards extra args to the underlying agent command
Supported Agents
Claude Code
- protocol:
anthropic - launch mode: environment variables
- supports both
ANTHROPIC_AUTH_TOKENandANTHROPIC_API_KEY - ensures onboarding is marked complete before launch
Codex
- protocol:
openai-responses - launch mode: temporary
CODEX_HOMEand generatedconfig.toml - does not read or merge any existing Codex home/config
Hermes Agent
- protocol:
openai-chat-completions - launch mode: temporary
HERMES_HOMEand generatedconfig.yaml - injects API key via environment variable
- does not read or merge any existing Hermes home/config
Crush
- protocol: prefers
openai-chat-completions, falls back toanthropic - launch mode: synchronizes managed providers into global
crush.json - injects provider keys with per-process environment variables
- writes Crush options to rely on synced providers and disable provider auto-update
Configuration
Default config path:
~/.config/agent-run/config.yaml
Minimal example:
providers:
deepseek:
protocols:
- openai-chat-completions
- anthropic
base_urls:
openai: https://api.deepseek.com
anthropic: https://api.deepseek.com/anthropic
key_command:
- printenv
- DEEPSEEK_API_KEY
default_model: deepseek-v4-pro
models:
- deepseek-v4-pro
model_api_filters: []
kimi-code:
protocols:
- openai-chat-completions
- anthropic
base_urls:
openai: https://api.kimi.com/coding/v1
anthropic: https://api.kimi.com/coding
key_command:
- printenv
- KIMI_API_KEY
anthropic_use_api_key: true
default_model: kimi-for-coding
models:
- kimi-for-coding
isolated_homes:
codex:
sandbox: {}
hermes:
sandbox: {}
See config.demo.yaml for a fuller example.
Protocol Negotiation
protocols declares what a provider can speak.
clauderequiresanthropiccodexrequiresopenai-responseshermesrequiresopenai-chat-completionscrushprefersopenai-chat-completionsand falls back toanthropic
For single-protocol agents, launch fails unless the provider supports that protocol or --force protocol is used.
For crush, agent-run picks the first supported protocol from that preference order.
Usage
Open or initialize your config:
agent-run config
agent-run config --bootstrap-config
agent-run config only opens an existing config. Use --bootstrap-config to write the embedded sample config first when the file does not exist.
Launch Claude:
agent-run launch deepseek claude
agent-run launch kimi-code claude
Launch Codex:
agent-run launch ollama codex
agent-run launch openrouter --model openai/gpt-5.3-codex codex
agent-run launch sandbox codex
Launch Hermes:
agent-run launch deepseek hermes
agent-run launch ollama hermes
agent-run launch sandbox hermes
isolated_homes notes:
isolated_homes.codex.<name>andisolated_homes.hermes.<name>allow launching that agent with an isolated runtime home even when no provider exists.- Entries are empty objects today. They do not accept
key,base_url,model, or custom paths. - When both a provider and an isolated home entry share the same name, agent-run combines them: isolated runtime home plus provider-derived runtime config.
- Runtime homes persist across launches. agent-run reuses the existing directory and only updates the generated config file.
Launch Crush:
agent-run launch deepseek crush
agent-run launch kimi-code crush run "explain this repository"
Generate shell completion:
agent-run completion bash
agent-run completion zsh
When installed from Nix, bash and zsh completion files are installed automatically.
Manual shell setup is only needed when running the binary outside the Nix package:
source <(agent-run completion bash)
source <(agent-run completion zsh)
Model catalog:
agent-run models list openrouter
agent-run models list --refresh openrouter
agent-run models list --all
Forward extra args to the underlying agent:
agent-run launch deepseek claude resume
agent-run launch ollama codex resume --last
agent-run launch deepseek hermes -- chat -q "hello"
Both forms are supported:
agent-run launch provider agent arg1 arg2agent-run launch provider agent -- arg1 arg2
Completion notes:
- Bash and Zsh are supported.
- Provider completion is loaded from local
config.yaml. --modelcompletion refreshes remote model cache by default whenmodel_api_filtersis enabled.- Set
AGENT_RUN_DISABLE_MODEL_COMPLETION_REFRESH=1to make completion use local models plus existing cache only. - Default log level is
WARN. Completion runs stay silent unless you explicitly setRUST_LOG. - Trailing
agent_argsare forwarded but are not completed.
Model API filter notes:
- Omit
model_api_filtersto use the default catch-all rule. - Set
model_api_filters: []ormodel_api_filters: nullto disable remote model loading and cache interaction for that provider. - Filters are applied to normalized remote models before cache write.
Secret Handling
Each provider can use either:
keykey_command
key_command is preferred when you want to fetch secrets from an external source such as a password manager, shell environment, or local secret helper.
Extra Environment Variables
Each provider may declare an extra_env map that is injected into the launched agent process. The map is applied last and may override the env vars agent-run sets by default (e.g. ANTHROPIC_API_KEY, CODEX_HOME, OPENAI_API_KEY).
Values support inline template expansion:
${env:NAME}— reads env varNAMEfrom the launcher process; errors if unset.${context:FIELD}— reads a resolved launch field. Supported fields:provider,protocol,model,key,agent,base_url.
Example:
providers:
openrouter:
# ...other fields...
extra_env:
OPENROUTER_API_KEY: "${context:key}"
HTTPS_PROXY: "${env:CORP_PROXY}"
AGENT_RUN_TRACE: "${context:agent}:${context:model}"
Runtime Strategy
agent-run avoids modifying long-lived agent config for single-provider agents. Crush is the intentional multi-provider exception: its global config is the place where configured providers live, while secrets are still injected at launch time.
claudeuses temporary env plus a one-time onboarding state fixcodexuses generated runtime config under cachehermesuses generated runtime config under cachecrushsynchronizes all compatibleproviders.*entries into~/.config/crush/crush.jsonand marks them as agent-run managed
Crush note:
- Existing non-agent-run Crush providers are preserved. If a provider name conflicts, rename one side or remove the unmanaged Crush entry.
- Managed Crush providers store API key placeholders such as
${DEEPSEEK_API_KEY:?...}; agent-run sets those env vars only for the launched process. - agent-run removes stale managed Crush providers only when they are no longer present in
agent-runconfig. A provider that temporarily fails to resolve is skipped for this launch but not deleted fromcrush.json. - agent-run does not pass a launch model to Crush. Crush does not currently support a general launch-time model override, so model selection is left to Crush defaults,
crush run --model, or the UI. - agent-run writes
options.disable_default_providers = trueandoptions.disable_provider_auto_update = trueincrush.json. - Managed Crush providers include the known model
idandnameso Crush has a usable local model list even when the provider API is unreachable; Crush can still discover models from the provider API at runtime. - Crush may still read the current project directory and initialize project-local files such as
AGENTS.md
Development
This repository uses a minimal Rust + Nix setup.
Useful commands:
cargo check
cargo clippy -- -D warnings
cargo fmt