Skip to content

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:

use:
  - no-force-push
  - {tests-before-push: {mode: warn}}

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.