The rule library¶
Generated from agentltl-coding/src/agentltl_coding/library/
It is kept with the code it documents. Edit it there.
Tested rules you switch on by name, instead of writing them. In a rule file:
In Claude Code and Mistral Vibe, /agentltl:setup lets you tick them; in Copilot CLI, /agentltl-setup; in Codex, $agentltl:setup. Each entry's rules get fixes with plugin updates; a rule of your own with the same id replaces the packaged one. An entry marked (Claude Code), (Copilot CLI), (Mistral Vibe) or (Codex) is for that agent only: elsewhere use: of it switches nothing on.
| Entry | What it does | Mode | Tags |
|---|---|---|---|
ask-before-creating-credentials |
Ask before minting new credentials (access keys, service-account keys, tokens, service principals). | ask | secrets, devops |
ask-before-discarding-work |
Ask before git commands that throw away uncommitted or unpushed work. | ask | git |
ask-before-infra-changes |
Ask before changing infrastructure (terraform apply/destroy, kubectl apply/delete/scale/drain, aws s3 rm). | ask | cloud |
ask-before-installing |
Ask before installing or adding packages (pip, npm, yarn, pnpm, uv, poetry, cargo, apt, brew, gem, go). | ask | dependencies |
ask-before-reading-secret-stores |
Ask before commands that print secrets from a store (kubectl secrets, vault, cloud secret managers, password managers, sops...). | ask | secrets, devops |
ask-before-recursive-delete |
Ask before any recursive delete (rm -r, rm -rf). | ask | files |
no-claude-coauthor |
Claude never signs commits, tags or notes (no Co-Authored-By, no Claude author). (Claude Code) | block | git |
no-codex-coauthor |
Codex never signs commits, tags or notes (no Co-authored-by, no "Generated with Codex"). (Codex) | block | git |
no-copilot-coauthor |
Copilot never signs commits, tags or notes (no Co-authored-by, no Copilot author). (Copilot CLI) | block | git |
no-env-dumps |
Never print the environment or a secret-looking variable (env, printenv, export -p, echo $TOKEN, docker inspect...). | block | secrets, devops |
no-force-push |
Never force-push (--force-with-lease is still allowed). | block | git |
no-leaky-debug |
Warn before debug output that prints credentials (curl -v with auth headers, kubectl -v=6+, aws/az --debug, gcloud --log-http). | warn | secrets, devops |
no-push-to-main |
Never push to main or master by name (a bare git push while on main is not seen). |
block | git |
no-secrets-in-commands |
Never put a secret value on the command line (passwords in flags, bearer headers, credentials in URLs). | block | secrets, devops |
no-skipping-secret-scans |
Ask before skipping git hooks (--no-verify, which also skips secret scanners) or force-adding ignored files. | ask | secrets, git, devops |
no-vibe-coauthor |
Mistral Vibe never signs commits, tags or notes (no Co-Authored-By, no "Generated by Mistral Vibe"). (Mistral Vibe) | block | git |
protect-env-files |
Never read, write, copy or delete .env files (.env.example is fine). | stop | secrets |
protect-secret-files |
Never read, copy, send or edit credential files (.env, keys, cloud and tool credentials, shell history). | stop | secrets, devops |
read-before-overwrite |
Read an existing file before overwriting or editing it in place. | block | editing |
subagents-on-sonnet |
Subagents run on Sonnet (or Haiku), never on a bigger model. (Claude Code) | block | cost, agents |
tests-before-finishing |
Before finishing a turn that edited code, run the tests (pytest, npm/cargo/go/make test, tox, jest). | block | testing |
tests-before-push |
Run the tests, and see them pass, after your last edit before pushing (pytest, npm/cargo/go/make test, tox, jest). | block | testing, git |
Bundles¶
| Bundle | Switches on |
|---|---|
devops-secrets |
protect-secret-files, no-env-dumps, ask-before-reading-secret-stores, ask-before-creating-credentials, no-secrets-in-commands, no-leaky-debug, no-skipping-secret-scans |
The rules in full¶
ask-before-creating-credentials¶
Ask before minting new credentials (access keys, service-account keys, tokens, service principals).
rules:
- id: ask-before-creating-credentials
never:
- aws_iam_create_access_key
- aws_iam_create_login_profile
- aws_sts_get_session_token
- aws_sts_assume_role
- gcloud_iam_service_accounts_keys_create
- az_ad_sp_create_for_rbac
- az_ad_sp_credential_reset
- vault_token_create
- gh_auth_refresh
why: New credentials are printed once and stay valid; the user should know they exist and decide
where they go.
mode: ask
ask-before-discarding-work¶
Ask before git commands that throw away uncommitted or unpushed work.
rules:
- id: ask-before-discarding-work
never:
- tool: git_reset
with:
hard: true
- tool: git_clean
with:
force: true
- tool: git_branch
with:
force_delete: true
- tool:
- git_stash_drop
- git_stash_clear
- tool:
- git_checkout
- git_restore
where:
paths: '*'
why: These commands discard work that may not exist anywhere else.
mode: ask
ask-before-infra-changes¶
Ask before changing infrastructure (terraform apply/destroy, kubectl apply/delete/scale/drain, aws s3 rm).
rules:
- id: ask-before-infra-changes
never:
- terraform_apply
- terraform_destroy
- kubectl_apply
- kubectl_delete
- kubectl_scale
- kubectl_drain
- kubectl_create
- aws_s3_rm
why: Infrastructure changes affect running systems and other people.
fix: Show the plan (terraform plan, kubectl diff) and let the user run it.
mode: ask
ask-before-installing¶
Ask before installing or adding packages (pip, npm, yarn, pnpm, uv, poetry, cargo, apt, brew, gem, go).
rules:
- id: ask-before-installing
never:
- pip_install
- tool: python
with:
module: pip
script: install
- tool:
- npm
- pnpm
- uv
- gem
- apt
- apt-get
- brew
- go
- pipx
with:
argv: install
- tool: npm
with:
argv: i
- tool:
- yarn
- pnpm
- uv
- poetry
- cargo
with:
argv: add
why: New dependencies change the supply chain and the lockfile; the user decides.
mode: ask
ask-before-reading-secret-stores¶
Ask before commands that print secrets from a store (kubectl secrets, vault, cloud secret managers, password managers, sops...).
rules:
- id: ask-before-reading-secret-stores
never:
- tool: kubectl_get
where:
resource:
- secret
- secrets
- secret/*
- secrets/*
- tool: kubectl_config_view
with:
raw: true
- - helm_get_values
- helm_get_all
- - terraform_output
- terraform_show
- terraform_state_pull
- terraform_console
- - aws_secretsmanager_get_secret_value
- aws_secretsmanager_batch_get_secret_value
- aws_configure_get
- aws_configure_export_credentials
- aws_ecr_get_login_password
- aws_kms_decrypt
- tool:
- aws_ssm_get_parameter
- aws_ssm_get_parameters
- aws_ssm_get_parameters_by_path
with:
with_decryption: true
- - gcloud_secrets_versions_access
- gcloud_auth_print_access_token
- gcloud_auth_print_identity_token
- gcloud_auth_application_default_print_access_token
- - az_keyvault_secret_show
- az_keyvault_secret_download
- az_keyvault_key_download
- az_account_get_access_token
- az_storage_account_keys_list
- az_storage_account_show_connection_string
- az_acr_credential_show
- - vault_kv_get
- vault_read
- vault_print_token
- vault_token_lookup
- - gh_auth_token
- tool: gh_auth_status
with:
show_token: true
- tool: git_credential
with:
action: fill
- - heroku_config
- heroku_config_get
- heroku_auth_token
- doppler_secrets
- doppler_secrets_get
- doppler_secrets_download
- vercel_env_pull
- tool: sops
with:
decrypt: true
- - ansible_vault_view
- ansible_vault_decrypt
- tool:
- gpg
- age
with:
decrypt: true
- tool: gpg
with:
export_secret_keys: true
- - op_read
- op_item_get
- bw_get
- bw_export
- pass_show
- secret_tool_lookup
- security_find_generic_password
- security_find_internet_password
- security_dump_keychain
- tool:
- openssl_rsa
- openssl_pkey
- openssl_ec
with:
text: true
- tool: openssl_pkcs12
with:
nodes: true
why: These commands print secret values, which then sit in the conversation and the transcript.
fix: If the value is only needed by another command, pass it without printing it (a pipe, or an
environment variable set by the tool), or ask the user.
mode: ask
ask-before-recursive-delete¶
Ask before any recursive delete (rm -r, rm -rf).
rules:
- id: ask-before-recursive-delete
never:
tool: rm
with:
recursive: true
why: A recursive delete cannot be undone.
mode: ask
no-claude-coauthor¶
Claude never signs commits, tags or notes (no Co-Authored-By, no Claude author).
rules:
- id: no-claude-coauthor
never:
- git_commit
- git_commit_tree
- git_tag
- git_notes
- git_merge
- git_revert
- git_cherry_pick
where:
'*':
- '*[Cc]o-[Aa]uthored-[Bb]y*'
- '*noreply@anthropic.com*'
- '*[Gg]enerated with*[Cc]laude*'
- '*[Cc]laude <*'
- user.name=*[Cc]laude*
- user.email=*anthropic.com*
why: Claude should not appear in git history.
fix: Drop the Co-Authored-By / "Generated with Claude Code" lines and commit as the user.
no-codex-coauthor¶
Codex never signs commits, tags or notes (no Co-authored-by, no "Generated with Codex").
rules:
- id: no-codex-coauthor
never:
- git_commit
- git_commit_tree
- git_tag
- git_notes
- git_merge
- git_revert
- git_cherry_pick
where:
'*':
- '*[Cc]o-[Aa]uthored-[Bb]y*'
- '*noreply@openai.com*'
- '*[Gg]enerated with*[Cc]odex*'
- '*[Cc]odex <*'
- user.name=*[Cc]odex*
- user.email=*openai.com*
why: Codex should not appear in git history.
fix: Drop the Co-authored-by / "Generated with Codex" lines and commit as the user.
no-copilot-coauthor¶
Copilot never signs commits, tags or notes (no Co-authored-by, no Copilot author).
rules:
- id: no-copilot-coauthor
never:
- git_commit
- git_commit_tree
- git_tag
- git_notes
- git_merge
- git_revert
- git_cherry_pick
where:
'*':
- '*[Cc]o-[Aa]uthored-[Bb]y*'
- '*Copilot@users.noreply.github.com*'
- '*[Gg]enerated *[Cc]opilot*'
- '*[Cc]opilot <*'
- user.name=*[Cc]opilot*
why: Copilot should not appear in git history.
fix: Drop the Co-authored-by / "Generated by Copilot" lines and commit as the user.
no-env-dumps¶
Never print the environment or a secret-looking variable (env, printenv, export -p, echo $TOKEN, docker inspect...).
rules:
- id: no-env-dumps
never:
- tool:
- env
- set
with:
argv: []
- printenv
- tool: export
with:
print: true
- tool: declare
with:
print: true
- tool: declare
with:
export: true
- tool:
- echo
- printf
where:
'*':
- '*$*TOKEN*'
- '*$*SECRET*'
- '*$*PASSWORD*'
- '*$*PASSWD*'
- '*$*API_KEY*'
- '*$*PRIVATE_KEY*'
- '*$*CREDENTIAL*'
- tool:
- kubectl_exec
- docker_exec
where:
command:
- env
- printenv
- docker_inspect
- docker_compose_config
why: Environment variables often hold credentials; printing them puts the secrets into the conversation.
fix: Check that a variable is set without printing it, e.g. `test -n "$VAR" && echo set`.
mode: block
no-force-push¶
Never force-push (--force-with-lease is still allowed).
rules:
- id: no-force-push
never:
tool: git_push
with:
force: true
why: Force-pushing rewrites shared history and can destroy other people's work.
fix: Pull and merge or rebase, then push normally, or use --force-with-lease.
no-leaky-debug¶
Warn before debug output that prints credentials (curl -v with auth headers, kubectl -v=6+, aws/az --debug, gcloud --log-http).
rules:
- id: no-leaky-debug
never:
- tool: curl
with:
verbose: true
where:
header:
- '*uthorization*'
- '*oken*'
- '*-Key*'
- tool: kubectl_*
where:
verbosity:
- '6'
- '7'
- '8'
- '9'
- 1?
- tool: aws_*
with:
debug: true
- tool: az_*
with:
debug: true
- tool: gcloud_*
with:
log_http: true
why: At these levels the tools print request headers, including bearer tokens and signatures.
fix: Drop the debug flag, or lower the verbosity (kubectl -v=5 or less).
mode: warn
no-push-to-main¶
Never push to main or master by name (a bare git push while on main is not seen).
rules:
- id: no-push-to-main
never:
tool: git_push
where:
refspecs:
- main
- master
- '*:main'
- '*:master'
why: main only changes through reviewed pull requests.
fix: Push to a feature branch and open a pull request.
no-secrets-in-commands¶
Never put a secret value on the command line (passwords in flags, bearer headers, credentials in URLs).
rules:
- id: no-secrets-in-commands
never:
- tool: curl
where:
header:
- '*uthorization: Bearer *'
- '*uthorization: Basic *'
- '*-Token: *'
- '*-Key: *'
- tool: curl
where:
user: '*:*'
- tool: '*'
where:
'*':
- '*://*:*@*'
- tool: mysql
where:
password_suffix: ?*
- tool: redis_cli
where:
password: ?*
- tool: docker_login
where:
password: ?*
- tool: kubectl_create
where:
from_literal: '*=*'
- tool:
- aws_secretsmanager_create_secret
- aws_secretsmanager_put_secret_value
where:
secret_string: ?*
- tool: aws_ssm_put_parameter
with:
type: SecureString
- tool:
- az_keyvault_secret_set
where:
value: ?*
- tool:
- gh_secret_set
where:
body: ?*
- tool:
- az_login
- az_acr_login
- kubectl_config_set_credentials
where:
password: ?*
- tool: kubectl_config_set_credentials
where:
token: ?*
why: Command lines end up in the conversation, the shell history and process listings.
fix: Read the secret from a file or an environment variable the tool supports (--password-stdin,
--from-file, -F secret=@file), or let the user run the command.
mode: block
no-skipping-secret-scans¶
Ask before skipping git hooks (--no-verify, which also skips secret scanners) or force-adding ignored files.
rules:
- id: no-skipping-secret-scans
never:
- tool:
- git_commit
- git_push
with:
no_verify: true
- tool: git_add
with:
force: true
why: Pre-commit hooks often run secret scanners, and ignored files often are the secrets.
mode: ask
no-vibe-coauthor¶
Mistral Vibe never signs commits, tags or notes (no Co-Authored-By, no "Generated by Mistral Vibe").
rules:
- id: no-vibe-coauthor
never:
- git_commit
- git_commit_tree
- git_tag
- git_notes
- git_merge
- git_revert
- git_cherry_pick
where:
'*':
- '*[Cc]o-[Aa]uthored-[Bb]y*'
- '*vibe@mistral.ai*'
- '*[Gg]enerated by*[Vv]ibe*'
- '*[Mm]istral [Vv]ibe <*'
- user.name=*[Vv]ibe*
- user.email=*mistral.ai*
why: Mistral Vibe should not appear in git history.
fix: Drop the Co-Authored-By / "Generated by Mistral Vibe" lines and commit as the user.
protect-env-files¶
Never read, write, copy or delete .env files (.env.example is fine).
rules:
- id: protect-env-files
never:
tool: '*'
where:
'*':
- .env
- .env.local
- .env.*.local
- .env.production
- .env.staging
- .env.development
why: .env files hold credentials; reading one puts secrets into the conversation.
fix: Ask the user for the variable names you need, or read .env.example.
mode: stop
protect-secret-files¶
Never read, copy, send or edit credential files (.env, keys, cloud and tool credentials, shell history).
rules:
- id: protect-secret-files
never:
tool: '*'
where:
'*':
- .env
- .env.local
- .env.*.local
- .env.production
- .env.staging
- .env.development
- .envrc
- '*.tfvars'
- terraform.tfstate*
- '*.pem'
- '*.key'
- '*.p12'
- '*.pfx'
- '*.jks'
- credentials.json
- service-account*.json
- '**/secrets.yaml'
- '**/secrets.yml'
- .htpasswd
- '**/.ssh/id_*'
- '**/.gnupg/*'
- '**/.aws/credentials'
- '**/.config/gcloud/*'
- '**/.azure/*'
- '**/.kube/config'
- '**/.docker/config.json'
- '**/.npmrc'
- '**/.pypirc'
- '**/.netrc'
- '**/.git-credentials'
- '**/.config/gh/hosts.yml'
- '**/.vault-token'
- '**/.pgpass'
- '**/.my.cnf'
- '**/.claude/.credentials.json'
- '**/.bash_history'
- '**/.zsh_history'
- /etc/shadow
- /proc/*/environ
why: These files hold credentials. Reading one puts the secret into the conversation and the transcript;
copying or sending one leaks it.
fix: Ask the user for what you need (a variable name, a non-secret value), or use the example file
(.env.example).
mode: stop
read-before-overwrite¶
Read an existing file before overwriting or editing it in place.
rules:
- id: read-before-overwrite
before:
first:
tool: Read
with:
file_path: $f
then:
- tool: Write
with:
file_path: $f
exists: true
- tool:
- sed
- perl
with:
in_place: true
paths: $f
exists: true
- tool: '*'
with:
overwrite_to: $f
exists: true
why: Overwriting a file you have not read loses whatever was in it.
fix: Read the file first, then write it.
subagents-on-sonnet¶
Subagents run on Sonnet (or Haiku), never on a bigger model.
rules:
- id: subagents-on-sonnet
require:
tool:
- Agent
- Task
where:
model:
- sonnet
- haiku
why: Subagents should run on a cheaper model than the main session.
fix: Pass model "sonnet" (or "haiku") to the Agent tool.
- id: subagents-on-sonnet-no-fork
never:
tool:
- Agent
- Task
with:
subagent_type: fork
why: A fork always runs on the main session's model, whatever model you pass.
fix: Start a fresh subagent (a subagent_type other than fork) with model "sonnet".
tests-before-finishing¶
Before finishing a turn that edited code, run the tests (pytest, npm/cargo/go/make test, tox, jest).
rules:
- id: tests-before-finishing
finally:
call:
- - pytest
- tox
- nox
- jest
- vitest
- tool:
- npm
- pnpm
- yarn
- cargo
- go
- make
- just
with:
argv: test
- tool:
- uv
- poetry
with:
argv: pytest
- tool: npx
with:
argv: jest
since:
- Edit
- Write
- MultiEdit
- NotebookEdit
why: Work handed back untested is work the user has to test.
fix: Run the test suite now. If the project tests differently, replace this rule with your own command.
tests-before-push¶
Run the tests, and see them pass, after your last edit before pushing (pytest, npm/cargo/go/make test, tox, jest).
rules:
- id: tests-before-push
before:
first:
- tool:
- pytest
- tox
- nox
- jest
- vitest
succeeded: true
- tool:
- npm
- pnpm
- yarn
- cargo
- go
- make
- just
with:
argv: test
succeeded: true
- tool:
- uv
- poetry
with:
argv: pytest
succeeded: true
- tool: npx
with:
argv: jest
succeeded: true
then: git_push
since:
- Edit
- Write
- MultiEdit
- NotebookEdit
why: Untested pushes break CI for everyone.
fix: Run the test suite after your last edit and make it pass, then push. If your project tests
differently, replace this rule with your own command.