Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Secure Agentic Dev

Hardened configurations for AI coding agents. Drop-in configs that let Claude Code, Codex CLI, Cursor, and Grok CLI read, stage, and commit — but never push, rewrite history, or destroy data.

Claude Code · Cursor · Codex CLI · Grok CLI · Skills · How it works · Getting started

Overview

AI coding agents are powerful but dangerous when given unrestricted shell access. A single hallucinated git push --force or git reset --hard can cause irreversible damage.

This repo provides production-ready dotfile configurations for three major AI coding agents, enforcing a strict commit-only workflow through three independent safety layers.

How it works

Every destructive operation is blocked at three layers simultaneously, so even a confused or adversarially-prompted agent cannot cause irreversible damage:

  1. Rules & instructions — the agent is told which commands are forbidden and why
  2. Permission deny-lists — the harness rejects matching commands before execution
  3. PreToolUse hooks — shell scripts parse the command payload and hard-block on match

Important

All three layers must be bypassed for a destructive command to execute. Each layer works independently — if one fails, the others still catch it.

The hook scripts are hardened against evasion: they strip single- and double-quoted spans before matching (so git commit -m "never git push" is not blocked), split compound commands on &&, ||, ;, |, and newlines to check each segment independently (so cd /tmp && git push still trips), and collapse git -c key=value prefixes so -c flags cannot smuggle a subcommand past the anchors.

scripts/test-git-block-hook keeps the rules and all four hooks in lockstep: it asserts a canonical set of dangerous commands is blocked and benign commands are allowed across every hook, and that each forbidden token in git.md has a matching pattern in all four hook scripts — so a new rule cannot drift out of sync with the enforcement. Run the full suite with make verify (lint + parity test + sync dry-runs).

Blocked commands

Category Commands
Git push git push, --force, --force-with-lease
Git history git reset, rebase, commit --amend
Git worktree git checkout --, restore, switch, clean -f, git worktree
Git deletion git branch -D, git rm, git stash (all forms)
Git recovery git gc, prune, reflog expire, update-ref
Destructive shell rm -rf, rm -fr, sudo, mkfs, dd
Pipe-to-shell curl | bash, wget | bash, curl | sh, wget | sh
Shell config edit † ~/.bashrc, ~/.zshrc, ~/.bash_profile, ~/.zprofile
Credential read † ~/.ssh, ~/.gnupg, ~/.aws, ~/.azure, ~/.config/gh, ~/.git-credentials, ~/.docker, ~/.kube, ~/.npmrc, ~/.pypirc, ~/.gem

Note

† The shell-config-edit row is enforced at the Claude permissions layer only — as Edit/Write deny rules in settings.json. Credential reads are enforced on Claude (Read deny rules) and Cursor (the beforeReadFile hook plus Read(...) denies in cli-config.json). The other rows are blocked at all three layers on every agent.

Claude Code

claude/.claude/
├── CLAUDE.md                  # rule loader + index
├── settings.json              # permissions, hooks, env, plugins
├── statusline-command.sh      # custom TUI status line
├── hooks/
│   └── block-dangerous-git.sh # PreToolUse hook (regex-based)
└── rules/
    ├── comments.md            # commenting style (scoped to code files)
    ├── communication.md       # brevity rules
    ├── development.md         # coding philosophy
    ├── git.md                 # forbidden commands
    ├── markdown.md            # output formatting (scoped to *.md/*.mdx)
    ├── models.md              # model routing for delegated work
    └── orchestration.md       # per-turn analyze → plan → delegate SOP

comments.md and markdown.md carry a paths: frontmatter block so Claude Code only loads them when editing matching files. models.md and orchestration.md are Claude-only delegation tooling — they are deliberately excluded from the generated Cursor rules.

Key settings in settings.json:

  • Telemetry disabled — DISABLE_ERROR_REPORTING, feedback survey off
  • Permissions — read-only git + staging allowed, all destructive ops denied
  • Hook — block-dangerous-git.sh fires on every Bash tool call
  • Attribution blanked — no "Generated with" or "Co-Authored-By" trailers

Cursor

cursor/.cursor/
├── hooks.json                    # hook registration (failClosed)
├── cli-config.json               # Cursor CLI enforced permissions + sandbox
├── permissions.json              # IDE-side allowlists (best-effort, JSONC)
└── hooks/
    ├── block-dangerous-git.sh    # beforeShellExecution — dangerous git/shell
    └── block-credential-reads.sh # beforeReadFile — blocks secret file reads
cursor/templates/
└── cursorignore                  # per-project .cursorignore template

Cursor is covered by three configuration layers, in decreasing order of enforcement:

  1. Hooks (hooks.json + hooks/) — the real enforcement. Both entries set "failClosed": true, so a hook that errors blocks rather than fails open. block-dangerous-git.sh runs on beforeShellExecution; block-credential-reads.sh runs on beforeReadFile and denies reads of secret material — .env*, *.pem, *.key, id_rsa*/id_ed25519*, and anything under ~/.ssh, ~/.aws, ~/.gnupg, ~/.azure, ~/.config/gh, ~/.kube, ~/.docker, plus .git-credentials. Both speak Cursor's JSON protocol ({"permission":"allow"} / {"permission":"deny","agent_message":"..."}) and fail closed if jq is missing.
  2. cli-config.json — enforced permissions for the Cursor CLI (cursor-agent): a Shell(...) deny-list mirroring the git blocklist plus sudo/mkfs/dd, Read(...) denies for credential paths, an allowlist of read-only git + build commands, approvalMode: "allowlist", and sandbox on.
  3. permissions.json — the IDE-side allowlist and auto-run guardrails. Best-effort, not a security boundary (see the comment at the top of the file); it steers the IDE agent but the hooks are what actually block.

Rules are generated at sync time from claude/.claude/rules/*.md — each .md becomes a .mdc with Cursor frontmatter injected and the Claude frontmatter stripped; rules with a paths: list become file-scoped (globs: + alwaysApply: false). models.md and orchestration.md are skipped. This keeps Claude as the single source of truth.

Note

Cursor does not read ~/.cursor/rules/*.mdc globally — global "User Rules" are UI-only with no backing file. The generated .mdc files exist for convenience: copy the ones you want into a project's .cursor/rules/. Likewise .cursorignore is per-project only; use the cursor/templates/cursorignore template as a starting point.

Codex CLI

codex/.codex/
├── AGENTS.md                  # combined behavioral rules
├── config.toml                # model, sandbox, privacy + inline PreToolUse hook
├── hooks/
│   └── block-dangerous-git.sh # PreToolUse hook (case-based)
└── rules/
    └── default.rules          # prefix_rule exec policy

Key settings in config.toml:

  • All telemetry off — analytics, feedback, OTEL exporters all disabled
  • Sandbox — workspace-write mode (can write files, cannot escape workspace)
  • Hook — registered inline under [[hooks.PreToolUse]] (with features.hooks = true); block-dangerous-git.sh runs before every Bash call. Codex reads this from config.toml directly — no separate hooks.json
  • Exec policy — default.rules enumerates every allowed and forbidden command with justifications
  • Attribution blanked — commit_attribution = ""

Grok CLI

grok/.grok/
├── AGENTS.md                  # combined behavioral rules
├── config.toml                # privacy, permissions, compat decoupling
└── hooks/
    ├── block-dangerous-git.sh # PreToolUse hook (deny-JSON protocol)
    └── git-safety.json        # PreToolUse hook registration

Key settings in config.toml:

  • Telemetry off — [features] telemetry/feedback and [telemetry] trace/mixpanel all disabled
  • Permissions — [permission] deny/allow lists mirror the Claude settings (read-only git + staging allowed, all destructive ops denied); deny wins over allow and holds even under always-approve
  • yolo = false — kept off, but the deny-list and hook still enforce even if a user flips it on
  • Compat decoupling — [compat.claude] / [compat.cursor] / [compat.codex] turn off foreign scans so Grok loads rules/hooks only from its own ~/.grok/ sources, avoiding double-loading
  • Hook — git-safety.json registers block-dangerous-git.sh as a Bash PreToolUse hook. Grok's runner fails open on anything but an explicit deny, so the hook emits a {"decision":"deny"} JSON on stdout (and exits 2) to block

Skills

skills/ holds hand-authored agent skills — self-contained instruction packages an agent loads on demand. Each is a directory with a SKILL.md (name + trigger description in frontmatter) and optional references/, scripts/, and gateways/.

Skill Purpose
bet Map every expected behavior of a unit into a .tree spec before writing tests
clean-ui Build and audit clean, geometrically correct web + mobile interfaces
effect-ts Idiomatic Effect-TS patterns — services/layers, errors, concurrency, schema, testing
fumadocs Write, refactor, and debug Fumadocs documentation sites idiomatically
handoff Compact the current conversation into a handoff doc + ready-to-paste kickoff prompt
rate Score work on quality axes, fix every gap, re-score until all axes hit 10
rate-iteration-cc The rate loop with an independent multi-model rater panel
vercel-observability Pull, query, and export Vercel observability data via CLI, Drains, and REST
delegate-grok Delegate from Grok to the Claude Code CLI for planning, review, and implementation

Each skill fans out to three consumer roots — ~/.claude/skills/, ~/.agents/skills/ (Codex/Cursor), and ~/.grok/skills/. A marker file in a skill directory narrows that fan-out:

  • .claude-only — only ~/.claude/skills/ (e.g. rate-iteration-cc, which shells out to the grok/codex CLIs)
  • .grok-only — only ~/.grok/skills/ (e.g. delegate-grok)
  • no marker — all three roots

Markers gate distribution and are never copied into the targets. bin/sync-skills copies (never symlinks — Codex's loader rejects symlinks) and tracks what it wrote in a per-target .sync-skills-manifest, so it only prunes skills it previously managed and leaves externally-installed ones untouched.

bin/sync-skills            # fan out all skills to the three roots
bin/sync-skills --dry-run  # preview
bin/sync-skills --unlink   # remove managed skills

Getting started

Option A: sync scripts (recommended)

The sync scripts use a split strategy — symlinks for files that benefit from live editing (settings, hooks), copies for files that would cause duplicate context loading if symlinked (CLAUDE.md, rules). This prevents AI agents from loading config twice when working inside this repo.

# Claude Code — selective symlinks + copies to ~/.claude/
bin/sync-claude

# Cursor — symlinks for hooks, generated .mdc rules from claude sources to ~/.cursor/
bin/sync-cursor

# Codex CLI — selective symlinks + copies to ~/.codex/
bin/sync-codex

# Grok CLI — symlinks for hooks, copies for AGENTS.md + config.toml to ~/.grok/
bin/sync-grok

# Skills — copy each skill to ~/.claude, ~/.agents, ~/.grok (honoring markers)
bin/sync-skills

To remove managed entries:

bin/sync-claude --unlink
bin/sync-cursor --unlink
bin/sync-codex --unlink
bin/sync-grok --unlink
bin/sync-skills --unlink

Preview without writing:

bin/sync-claude --dry-run
bin/sync-cursor --dry-run
bin/sync-codex --dry-run
bin/sync-grok --dry-run
bin/sync-skills --dry-run

After syncing, run make verify to lint the shell scripts, run the hook parity test, and dry-run all four sync scripts.

Option B: plain copy

cp -r claude/.claude ~/   # Claude Code
cp -r cursor/.cursor ~/   # Cursor
cp -r codex/.codex ~/     # Codex CLI
cp -r grok/.grok ~/       # Grok CLI

Option C: GNU Stow

# From the repo root — works but causes duplicate context loading when
# editing inside this repo (AI agents resolve symlinks)
stow claude
stow codex

Note

The hook scripts require jq to parse tool call payloads. They fail closed — if jq is missing, all commands are blocked rather than silently allowed.

Customization

  • Add allowed commands — edit permissions.allow in settings.json (Claude) or add prefix_rule(..., decision="allow") in default.rules (Codex)
  • Unblock a git command — remove it from all three layers (rules, deny-list, hook script) to maintain consistency
  • Add rules — drop a .md file in claude/.claude/rules/ (cursor .mdc rules are auto-generated on next bin/sync-cursor) or append to codex/.codex/AGENTS.md
  • Plugins — edit enabledPlugins in settings.json (Claude) or configure in config.toml (Codex)

Credits

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages