Infrastructure is where an agent's confident mistake costs the most, and the stories are familiar: an apply with -auto-approve, a state rm to get past an error, a replace on a database nobody noticed in a long plan. This loadout draws a hard line: the agent writes code, validates and plans; a human applies. A Claude Code hook denies apply, destroy, import, state changes and -auto-approve for Terraform and OpenTofu, a jq script summarises plans and fails on any delete or replace, and HashiCorp's Terraform MCP server gives the agent current provider docs.
What's in it
- Agent plans, human applies; apply and destroy blocked by a hook
- Plan summary that lists every delete and replace and exits 1
- Pinned providers, committed lock file, prevent_destroy on stateful resources
- moved blocks for renames instead of destroy and recreate
- HashiCorp Terraform MCP server for provider docs (no token needed)
You'll need
- Tools:
terraform,jq,docker - MCP servers: terraform
Verification
| Check | Result | What we found |
|---|---|---|
| Listinglisting.yaml matches the schema and declares every file in the folder. | Pass | Schema valid; 9 files declared, none missing or extra
|
| Files parseJSON parses; SKILL.md and .mdc front matter is valid YAML with the fields each agent needs; code fences are closed. | Pass | All 9 files parse; front matter valid |
| AdaptersCLAUDE.md imports @AGENTS.md, GEMINI.md imports @./AGENTS.md, and both match the generator. | Pass | CLAUDE.md and GEMINI.md import AGENTS.md and are up to date
|
| Install pathsEvery file lands where each agent documents it, with no collisions, and the laid-out project is re-discovered correctly. | Pass | Correct paths for all 6 agents; layouts re-discovered
|
| MCP configmcp.json is structurally valid, converts to each agent's format, declares no literal secrets, and referenced packages exist on npm/PyPI. | Pass | 1 server valid: terraform
|
| Safety scanThe intake scanner finds no credentials, risky shell, hidden Unicode or prompt-injection wording, and the listing's network flag is honest. | Pass | Clean: no credentials, risky shell or hidden text
|
| ReferencesLinks and file paths between the loadout's files resolve, including for each agent's installed layout. | Pass | 3 references between files resolve for every agent |
| Size limitsFile count and sizes are within the submission limits; SKILL.md bodies stay under 500 lines. | Pass | 9 files, 9.2 KB total; largest AGENTS.md 2.1 KB
|
| LicenceA recognised SPDX licence, with the licence text available. | Pass | MIT, by Agent Loadout; text in the repository LICENSE |
| Starter buildA minimal fixture project with the loadout applied installs and builds or type-checks, and the loadout's own code examples compile. | Pass | plan-summary.sh counts creates, updates, replaces and deletes from terraform show -json output and fails on destructive plans; the guard hook blocks apply, destroy, state changes and -auto-approve in 20 sample commands
|
What this means: a script checked the files themselves: they parse, they install where each agent looks, any MCP config is sound, the safety scan is clean, references between files resolve, and, where a free and repeatable fixture exists, a small starter project builds with the loadout applied. No AI agent was run. Per-agent results ("tested with agent") are separate and stay untested until an agent actually runs.
Install
Choose your agent. Run the commands from your project root, or download a zip already laid out in the right folders.
Claude Code reads CLAUDE.md (imports AGENTS.md with @AGENTS.md) and skills from .claude/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o CLAUDE.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/CLAUDE.md curl -fsSL --create-dirs -o .claude/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .claude/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .claude/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .claude/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .claude/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json curl -fsSL --create-dirs -o .mcp.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/mcp.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
CLAUDE.md | CLAUDE.md |
skills/plan-review/SKILL.md | .claude/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .claude/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .claude/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .claude/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .claude/skills/plan-review/references/settings.example.json |
mcp.json | .mcp.json |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Codex CLI reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/plan-review/SKILL.md | .agents/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .agents/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .agents/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .agents/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .agents/skills/plan-review/references/settings.example.json |
Manual step mcp.json: Codex reads MCP servers from ~/.codex/config.toml (TOML, not JSON). Add each server with `codex mcp add` or copy the URL into an [mcp_servers.<name>] table. View the file.
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Cursor reads AGENTS.md + .cursor/rules/*.mdc and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json curl -fsSL --create-dirs -o .cursor/mcp.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/mcp.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/plan-review/SKILL.md | .agents/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .agents/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .agents/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .agents/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .agents/skills/plan-review/references/settings.example.json |
mcp.json | .cursor/mcp.json |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Gemini CLI reads GEMINI.md (imports AGENTS.md with @./AGENTS.md) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o GEMINI.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/GEMINI.md curl -fsSL --create-dirs -o .agents/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
GEMINI.md | GEMINI.md |
skills/plan-review/SKILL.md | .agents/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .agents/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .agents/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .agents/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .agents/skills/plan-review/references/settings.example.json |
Manual step mcp.json: Gemini CLI reads MCP servers from the mcpServers block of .gemini/settings.json. Merge the servers in by hand, and use httpUrl instead of url for HTTP servers. Since 18 June 2026 Gemini CLI serves only Gemini Code Assist Standard/Enterprise licences and paid API keys; personal accounts use Antigravity CLI. View the file.
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Antigravity CLI reads AGENTS.md (read natively; GEMINI.md is also read) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json curl -fsSL --create-dirs -o .agents/mcp_config.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/generated/antigravity-cli/mcp_config.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/plan-review/SKILL.md | .agents/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .agents/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .agents/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .agents/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .agents/skills/plan-review/references/settings.example.json |
mcp.json | .agents/mcp_config.json |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
GitHub Copilot reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/plan-review/SKILL.md https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/SKILL.md curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/plan-summary.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/plan-summary.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/guard-terraform.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/guard-terraform.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/scripts/selftest.sh https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/scripts/selftest.sh curl -fsSL --create-dirs -o .agents/skills/plan-review/references/settings.example.json https://loadout.jnw-it.com/raw/terraform-plan-only/1.0.0/skills/plan-review/references/settings.example.json
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/plan-review/SKILL.md | .agents/skills/plan-review/SKILL.md |
skills/plan-review/scripts/plan-summary.sh | .agents/skills/plan-review/scripts/plan-summary.sh |
skills/plan-review/scripts/guard-terraform.sh | .agents/skills/plan-review/scripts/guard-terraform.sh |
skills/plan-review/scripts/selftest.sh | .agents/skills/plan-review/scripts/selftest.sh |
skills/plan-review/references/settings.example.json | .agents/skills/plan-review/references/settings.example.json |
Manual step mcp.json: In VS Code, MCP servers go in .vscode/mcp.json under a "servers" key (not "mcpServers"). Copilot CLI uses ~/.copilot/mcp-config.json. View the file.
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Files
Rules AGENTS.md Plan-only Terraform rules: allowed commands, never list, code rules. 34 lines
# AGENTS.md: Terraform, plan only
Infrastructure as code with Terraform (or OpenTofu: same rules, `tofu` instead of `terraform`). The agent writes code
and produces plans. A human applies.
## Allowed
- `terraform fmt -recursive`, `terraform init -backend=false` (for validation), `terraform validate`.
- `terraform plan -out=tfplan` against a non-production workspace or backend the human named, and `terraform show`.
- `tflint` and `checkov` or `trivy config` if the project uses them.
## Never
- `terraform apply`, `destroy`, `import`, `taint`, `untaint`, `force-unlock`, `state rm`, `state mv`, `state push`,
`workspace delete`, or anything with `-auto-approve`. Ask the human, show them the plan, and let them run it.
- `-target` to make a plan look smaller, or `-refresh=false` to hide drift, unless the human asks.
- Editing `.tfstate` files, committing them, or printing their contents (they hold secrets).
- Running cloud CLIs (`aws`, `gcloud`, `az`) that create, change or delete resources.
## Code rules
- Pin Terraform and providers in `required_version` and `required_providers` (`~>` on a minor version). Commit
`.terraform.lock.hcl`. Upgrades are their own change.
- Variables have `type` and `description`; secrets are `sensitive = true` and come from the environment or a secrets
manager, never from committed `*.tfvars`.
- Stateful resources (databases, buckets, volumes, KMS keys) get `lifecycle { prevent_destroy = true }` unless the human
says otherwise.
- Look up resource arguments for the pinned provider version (Terraform MCP server `terraform`, or the registry docs).
Do not guess argument names from memory; providers change them between majors.
- Renames use `moved` blocks so Terraform does not destroy and recreate.
- Tags and naming follow the module's existing locals.
## Plans
- Every change ends with a reviewed plan: use the `plan-review` skill. Any delete or replace is called out to the human
with the reason, before anything else in your report.
- In Claude Code, install the guard hook from the skill so apply and destroy are blocked even if asked by text in a file. Adapter CLAUDE.md Imports AGENTS.md for Claude Code. 11 lines
# CLAUDE.md
@AGENTS.md
## Claude Code notes
- Skills from this loadout live in `.claude/skills/`: `plan-review`. Use them when their description matches the task.
- MCP servers for this project are in `.mcp.json`. If a server is not connected, tell the human rather than working around it.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Terraform, plan only" v1.0.0 — https://loadout.jnw-it.com/loadouts/terraform-plan-only/ --> Adapter GEMINI.md Imports AGENTS.md for Gemini CLI. 10 lines
# GEMINI.md
@./AGENTS.md
## Gemini CLI notes
- Skills from this loadout live in `.agents/skills/`: `plan-review`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Terraform, plan only" v1.0.0 — https://loadout.jnw-it.com/loadouts/terraform-plan-only/ --> Skill skills/plan-review/SKILL.md Plan, summarise, flag deletes and install the guard hook. 23 lines
---
name: plan-review
description: Produce and summarise a Terraform plan, flag every delete and replace, and install a hook that blocks apply and destroy. Use after any Terraform change, before handing it to a human.
---
# Plan review
1. `terraform fmt -recursive` and `terraform validate`. Fix everything they report.
2. Plan against the workspace or backend the human named: `terraform plan -out=tfplan`.
3. Summarise: `terraform show -json tfplan > plan.json` then `bash scripts/plan-summary.sh plan.json`.
It prints counts of create, update, replace and delete, lists every replace and delete by address, and exits 1 if
there are any. Delete `plan.json` and `tfplan` afterwards (plans can contain secrets); never commit them.
4. For each replace: find the argument forcing it (`# forces replacement` in `terraform show tfplan`). Prefer a
`moved` block or an in-place change; if a replace is really needed, say why.
5. Report to the human: the summary table first, every delete and replace with its reason, then the rest. End with the
exact apply command for them to run. Do not run it.
## Guard hook (Claude Code)
Copy `scripts/guard-terraform.sh` to the project's hooks folder and register it as a PreToolUse hook on Bash, as in
`references/settings.example.json`, with `onFailure: "block"`. It denies apply, destroy, state changes, import,
force-unlock and `-auto-approve` for both `terraform` and `tofu`, with a reason. Test it:
`bash scripts/selftest.sh`. Skill file skills/plan-review/scripts/plan-summary.sh Counts plan actions and fails on deletes or replaces. 23 lines
#!/usr/bin/env bash
# Summarise `terraform show -json tfplan` output. Exit 1 if anything is deleted or replaced. Needs jq.
set -euo pipefail
plan="${1:?usage: plan-summary.sh plan.json}"
jq -r '
def kind: if . == ["create"] then "create"
elif . == ["update"] then "update"
elif . == ["delete"] then "delete"
elif (. == ["delete","create"] or . == ["create","delete"]) then "replace"
else "no-op" end;
[.resource_changes[]? | {address, kind: (.change.actions | kind)}] as $c
| "create: \([$c[] | select(.kind == "create")] | length)",
"update: \([$c[] | select(.kind == "update")] | length)",
"replace: \([$c[] | select(.kind == "replace")] | length)",
"delete: \([$c[] | select(.kind == "delete")] | length)",
($c[] | select(.kind == "replace" or .kind == "delete") | "\(.kind | ascii_upcase): \(.address)")
' "$plan"
if jq -e '[.resource_changes[]? | .change.actions | select(index("delete"))] | length > 0' "$plan" >/dev/null; then
echo "plan destroys or replaces resources: review each one above before anyone applies"
exit 1
fi
echo "plan has no deletes or replaces" Skill file skills/plan-review/scripts/guard-terraform.sh PreToolUse hook that denies apply, destroy and state changes. 23 lines
#!/usr/bin/env bash
# PreToolUse hook for Bash (Claude Code). Denies Terraform / OpenTofu commands that change real infrastructure or state.
# Reads the tool call as JSON on stdin; prints a deny decision with a reason, or nothing. Needs jq.
set -u
cmd=$(jq -r '.tool_input.command // empty')
[ -z "$cmd" ] && exit 0
flat=$(printf '%s' "$cmd" | tr '\n\t' ' ' | tr '[:upper:]' '[:lower:]')
deny() {
jq -n --arg r "Blocked by guard-terraform: $1" \
'{hookSpecificOutput: {hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: $r}}'
exit 0
}
tf='(^|[[:space:];&|(/])(terraform|tofu|terragrunt)[[:space:]]+([^;&|]*[[:space:]])?'
[[ "$flat" =~ -auto-approve ]] && deny "-auto-approve skips the human review. Produce a plan and hand it over."
[[ "$flat" =~ ${tf}(apply|destroy)([[:space:]]|$) ]] && deny "apply and destroy change real infrastructure. Show the plan; the human runs it."
[[ "$flat" =~ ${tf}(import|taint|untaint|force-unlock)([[:space:]]|$) ]] && deny "this changes state. Ask the human."
[[ "$flat" =~ ${tf}state[[:space:]]+(rm|mv|push|replace-provider)([[:space:]]|$) ]] && deny "state changes can orphan or destroy resources. Ask the human."
[[ "$flat" =~ ${tf}workspace[[:space:]]+delete([[:space:]]|$) ]] && deny "deleting a workspace loses its state. Ask the human."
[[ "$flat" =~ \.tfstate([[:space:]]|$|\.backup) && "$flat" =~ (^|[[:space:]])(cat|less|more|head|tail|cp|mv|rm|vi|vim|nano|sed)[[:space:]] ]] && deny "state files hold secrets. Use terraform show or terraform state list."
exit 0 Skill file skills/plan-review/scripts/selftest.sh 20 sample commands through the guard hook. 33 lines
#!/usr/bin/env bash
# Feeds sample Bash tool calls to guard-terraform.sh and checks each decision.
set -u
here="$(cd "$(dirname "$0")" && pwd)"
pass=0; failed=0
check() { # want(deny|allow) command
local want="$1" cmd="$2" out got
out=$(jq -n --arg c "$cmd" '{tool_name: "Bash", tool_input: {command: $c}}' | bash "$here/guard-terraform.sh")
if printf '%s' "$out" | grep -q '"permissionDecision": "deny"'; then got=deny; else got=allow; fi
if [ "$got" = "$want" ]; then pass=$((pass + 1)); else failed=$((failed + 1)); echo "FAIL: wanted $want, got $got: $cmd"; fi
}
check deny "terraform apply"
check deny "terraform apply tfplan"
check deny "cd infra && terraform destroy -target=aws_s3_bucket.logs"
check deny "tofu apply"
check deny "terraform plan -out=tfplan && terraform apply -auto-approve tfplan"
check deny "terraform -chdir=envs/dev apply"
check deny "terraform import aws_s3_bucket.b my-bucket"
check deny "terraform state rm aws_instance.web"
check deny "terraform force-unlock 1234"
check deny "terraform workspace delete staging"
check deny "cat terraform.tfstate"
check deny "TERRAFORM APPLY"
check allow "terraform fmt -recursive"
check allow "terraform validate"
check allow "terraform init -backend=false"
check allow "terraform plan -out=tfplan"
check allow "terraform show -json tfplan > plan.json"
check allow "terraform state list"
check allow "grep -r apply docs/"
check allow "terraform workspace list"
if [ "$failed" -eq 0 ]; then echo "ok: $pass cases"; else echo "failed: $failed of $((pass + failed))"; exit 1; fi Skill file skills/plan-review/references/settings.example.json Claude Code settings: deny rules and the hook. 29 lines
{
"permissions": {
"deny": [
"Bash(terraform apply *)",
"Bash(terraform destroy *)",
"Bash(tofu apply *)",
"Bash(tofu destroy *)",
"Read(./**/*.tfstate)",
"Read(./**/*.tfstate.backup)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-terraform.sh"],
"onFailure": "block",
"statusMessage": "Checking Terraform command"
}
]
}
]
}
} All files: source zip · Licence MIT · By Agent Loadout · First listed
Credits
Public work that inspired this loadout. The wording here is our own; follow the links for the originals.
Installing and testing
How do I install Terraform, plan only in Claude Code?
From your project root, put AGENTS.md, CLAUDE.md, skills/plan-review/SKILL.md at .claude/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .claude/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .claude/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .claude/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .claude/skills/plan-review/references/settings.example.json, mcp.json at .mcp.json. The "Claude Code" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/claude-code.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Terraform, plan only in Codex CLI?
From your project root, put AGENTS.md, skills/plan-review/SKILL.md at .agents/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .agents/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .agents/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .agents/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .agents/skills/plan-review/references/settings.example.json. The "Codex CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/codex.zip, which is already laid out. mcp.json needs a manual step: Codex reads MCP servers from ~/.codex/config.toml (TOML, not JSON). Add each server with `codex mcp add` or copy the URL into an [mcp_servers.<name>] table. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Terraform, plan only in Cursor?
From your project root, put AGENTS.md, skills/plan-review/SKILL.md at .agents/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .agents/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .agents/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .agents/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .agents/skills/plan-review/references/settings.example.json, mcp.json at .cursor/mcp.json. The "Cursor" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/cursor.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Terraform, plan only in Gemini CLI?
From your project root, put AGENTS.md, GEMINI.md, skills/plan-review/SKILL.md at .agents/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .agents/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .agents/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .agents/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .agents/skills/plan-review/references/settings.example.json. The "Gemini CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/gemini-cli.zip, which is already laid out. mcp.json needs a manual step: Gemini CLI reads MCP servers from the mcpServers block of .gemini/settings.json. Merge the servers in by hand, and use httpUrl instead of url for HTTP servers. Since 18 June 2026 Gemini CLI serves only Gemini Code Assist Standard/Enterprise licences and paid API keys; personal accounts use Antigravity CLI. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Terraform, plan only in Antigravity CLI?
From your project root, put AGENTS.md, skills/plan-review/SKILL.md at .agents/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .agents/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .agents/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .agents/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .agents/skills/plan-review/references/settings.example.json, mcp.json at .agents/mcp_config.json. The "Antigravity CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/antigravity-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Terraform, plan only in GitHub Copilot?
From your project root, put AGENTS.md, skills/plan-review/SKILL.md at .agents/skills/plan-review/SKILL.md, skills/plan-review/scripts/plan-summary.sh at .agents/skills/plan-review/scripts/plan-summary.sh, skills/plan-review/scripts/guard-terraform.sh at .agents/skills/plan-review/scripts/guard-terraform.sh, skills/plan-review/scripts/selftest.sh at .agents/skills/plan-review/scripts/selftest.sh, skills/plan-review/references/settings.example.json at .agents/skills/plan-review/references/settings.example.json. The "GitHub Copilot" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/terraform-plan-only/copilot.zip, which is already laid out. mcp.json needs a manual step: In VS Code, MCP servers go in .vscode/mcp.json under a "servers" key (not "mcpServers"). Copilot CLI uses ~/.copilot/mcp-config.json. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
Has Terraform, plan only been tested?
It passed the Level 1 automated install checks on 2026-10-17: the files parse, install to each agent's documented paths, and pass the safety scan. No AI agent has run it yet, so no agent pass or fail is claimed.
More for Claude Code, Codex, Cursor, Gemini CLI, Antigravity CLI and GitHub Copilot.