p.enthalabs

GitHub - RomanVolkov/agentctl: Terraform for your agent harnesses

![Image 1: Rust](https://www.rust-lang.org/)![Image 2: GitHub](https://github.com/RomanVolkov/agentctl)![Image 3: License: MIT](https://opensource.org/licenses/MIT)![Image 4: Works with OpenCode](https://opencode.com/)![Image 5: Works with Agy](https://gemini.google.com/)![Image 6: Works with Claude Code](https://claude.ai/code)![Image 7: Works with Codex](https://openai.com/codex)

Overview

[](https://github.com/RomanVolkov/agentctl#overview)

`agentctl` is a command-line tool that manages AI agent harness configurations from a single declarative `workspace.toml`. It reads one source of truth and compiles it into the native config directories each harness expects, so the same setup works across tools and machines.

The tool follows a Terraform-like workflow: `validate` checks the workspace, `preview` shows diffs before anything is written, and `apply` writes changes after confirmation.

It targets OpenCode, Agy (Antigravity CLI), Claude Code, and Codex. Each harness is a single renderer module, so adding or adjusting support stays isolated.

What it manages

[](https://github.com/RomanVolkov/agentctl#what-it-manages)

- **Model** — a default model at `[global]`, overridable per harness (each harness has its own model family).

- **Context** — `system_prompt`, applied where the native config supports instructions.

- **Skills** — local skill directories (`SKILL.md` plus scripts) copied into each harness's `skills/` folder.

- **Guardrails** — `allowed_dirs`, `auto_approve`, `require_approval` declared once; mapped to each harness's permission schema (Agy `command(...)`, Claude `Bash(...)`, Codex `approval_policy`).

- **Harness-specific flags** — kept under each harness's `extra`, passed through to the native config.

Two rules keep the setup unified:

- Settings are declared once at `[global]` and overridden per harness where they differ. A harness omits what it inherits. Set `skills = []` to override a default with nothing.

- Values support `$KEY` / `${KEY}` environment expansion at render time, so secrets stay out of the file.

Getting started

[](https://github.com/RomanVolkov/agentctl#getting-started)

Prerequisites

[](https://github.com/RomanVolkov/agentctl#prerequisites)

- Rust 1.85 or newer, or just the released `agentctl` binary.

Install

[](https://github.com/RomanVolkov/agentctl#install) Build from source:

cargo install --path agentctl

Create a workspace

[](https://github.com/RomanVolkov/agentctl#create-a-workspace) A `workspace.toml` lives at the root of a folder that represents the configuration (commonly the project itself):

[global] model = "claude-sonnet-4" max_spend_limit = 5.0 system_prompt = "You are a senior software engineer." skills = ["plan_make"] allowed_dirs = ["./src", "./tests"] auto_approve = ["git status", "ls", "cargo test"] require_approval = ["git push", "npm install"]

[skills.plan_make] type = "skill" path = "skills/plan-make"

[[harnesses]] provider = "opencode" config_path = "~/.dotfiles/opencode" extra = { instructions = ["{env:HOME}/.benjamin-plus/injected-instruction.md"] }

[[harnesses]] provider = "agy" model = "Gemini 3.7 Flash (Medium)" # overrides the global model config_path = "~/.gemini/antigravity-cli" extra = { allowNonWorkspaceAccess = true, enableTelemetry = false }

Skill `path` values may be relative to the workspace file or absolute. `config_path` points to a harness directory, not a single file, and supports `~` and `$HOME`.

Validate

[](https://github.com/RomanVolkov/agentctl#validate)

agentctl validate

Parses the workspace and checks that skill references resolve and providers are known. Useful in CI.

Preview

[](https://github.com/RomanVolkov/agentctl#preview)

agentctl preview

Renders every harness and prints colored unified diffs for each generated file, plus a summary of skill directory changes. Nothing is written.

Apply

[](https://github.com/RomanVolkov/agentctl#apply)

agentctl apply # shows diffs, then prompts agentctl apply --yes # writes without prompting

Before any change, `apply` copies the harness directory into a timestamped backup (`<dir>.<nanos>.bak`). Files are written atomically (temp file plus rename).

All commands accept `-c <path>` to point at a workspace file; the default is `./workspace.toml`.

Repository structure

[](https://github.com/RomanVolkov/agentctl#repository-structure)

``` agentctl/ ├── agentctl-core/ # library: model, parsing, rendering │ └── src/ │ ├── model.rs # workspace data model and validation │ ├── parse.rs # TOML parsing entry points │ ├── expand.rs # env var expansion │ └── render/ # Renderer trait + per-harness impls │ ├── opencode.rs # OpenCode renderer │ ├── agy.rs # Agy renderer │ ├── claude.rs # Claude Code renderer │ └── codex.rs # Codex renderer ├── agentctl/ # CLI binary │ └── src/ │ ├── cli.rs # clap-based CLI definition │ ├── commands/ # validate, preview, apply, common │ └── output.rs # colored unified diffs ├── config/ # example workspace for this repo │ ├── workspace.toml │ └── skills/ # local skill copies └── Cargo.toml # Cargo workspace ```

Supported harnesses

[](https://github.com/RomanVolkov/agentctl#supported-harnesses) | Provider | Rendered files | Notes | | --- | --- | --- | | `opencode` | `opencode.jsonc`, `system_prompt.md`, `skills/` | Provider config managed manually for now | | `agy` | `settings.json`, `skills/` | `system_prompt`/`require_approval` not emitted | | `claude` | `settings.json`, `skills/` | Guardrails map to `permissions.allow`/`ask` | | `codex` | `config.toml`, `skills/` | Guardrails approximated by `approval_policy`/`sandbox_mode` |

Primitives a harness cannot express are reported as `// not rendered: ...` notices during `preview`.

Reporting issues

[](https://github.com/RomanVolkov/agentctl#reporting-issues) Found a bug, typo, or have a suggestion? Open an issue at github.com/RomanVolkov/agentctl/issues.

Please include a description of the problem, steps to reproduce, and expected versus actual behavior.

License

[](https://github.com/RomanVolkov/agentctl#license) Licensed under the MIT License.

Acknowledgements

[](https://github.com/RomanVolkov/agentctl#acknowledgements) Built with the Rust ecosystem:

- clap — CLI argument parsing

- serde — serialization

- toml — TOML parsing

- similar — text diffs

- colored — terminal output

Contact

[](https://github.com/RomanVolkov/agentctl#contact) **Roman Volkov**

- GitHub: @RomanVolkov

- Repository: RomanVolkov/agentctl

Questions, suggestions, or collaboration? Open an issue on GitHub.