{"slug": "make-your-codebase-safe-for-ai-agents-in-one-command", "title": "Make your codebase safe for AI agents in one command", "summary": "A developer released agent-starter, an open-source tool that enforces safety hooks and linting rules on AI coding agents in a single command. The tool blocks dangerous commands like git push --force, catches silent error swallowing patterns, and runs lint on every edit to prevent common agent failures before they reach production.", "body_md": "AI coding agents are fast. If you've reviewed enough agent-written diffs, though, you've seen the same short list of failures show up again and again:\n\n`await`\n\nthat turns an async call into a race you only find in production.`as any`\n\nsprinkled in wherever the types didn't line up.`package.json`\n\n.`try/except: pass`\n\nwrapped around failing code to make a red test go green.`git reset --hard`\n\nthat eats uncommitted work.You can keep catching these in review, one diff at a time, forever. Or you can make the codebase itself push back the moment the agent writes them. That second option is what [agent-starter](https://github.com/sneg55/agent-starter) sets up, and most of it lands in one command.\n\n```\n/plugin marketplace add sneg55/agent-starter\n/plugin install agent-starter@agent-starter\n```\n\nThat wires five enforcement hooks and loads eight slash commands. Then you point it at a codebase: `/new-project`\n\nfor a fresh repo, `/adopt-project`\n\nto retrofit an existing one (it audits first, everything is opt-in, nothing gets overwritten).\n\nThe rest of this post is what that command actually does, and why each piece is there.\n\nClaude Code hooks run shell scripts at defined points in the agent's loop: before a Bash command, after a file write, at session start. agent-starter ships them as small, dependency-light bash scripts. The ones that earn their keep:\n\n`block-dangerous-commands.sh`\n\nruns *before* any Bash command executes. It blocks the short list of things that destroy work you can't get back:\n\n`git push --force`\n\n(it points you at `--force-with-lease`\n\ninstead)`git reset --hard`\n\n/ `--merge`\n\n`git clean -f`\n\n, `git checkout -- .`\n\n, `git restore .`\n\n`rm`\n\non `/`\n\n, `~`\n\n, or `$HOME`\n\n`chmod -R 777 /`\n\nWhen it fires, the agent sees this on stderr and stops:\n\n```\nBlocked dangerous command:\n  git push --force origin main\n\nWhy: 'git push --force' rewrites remote history - use --force-with-lease.\n```\n\nThere's an escape hatch (`CLAUDE_ALLOW_DANGEROUS=1`\n\n) for the rare time you actually mean it. The goal isn't to babysit. It's to make the destructive path require a deliberate override instead of a stray token in a long autonomous run.\n\n`check-silent-errors.sh`\n\nruns *after* every write. LLMs reach for `try/except`\n\nto make a failing test pass, but the code still breaks, just quietly, in a way nobody notices until later. The hook blocks the patterns that swallow errors:\n\n`except:`\n\n, `except: pass`\n\n, `except: ...`\n\nin Python`catch {}`\n\nin JS/TS`catch`\n\nblock whose only body is `console.log`\n\n(logging an error at info severity is still swallowing it)Every handler has to do something real: re-raise, return a sentinel, or log with context via `console.error`\n\n. One-off exceptions are explicit. You drop a `// silent-ok`\n\n(or `# silent-ok`\n\n) comment and the hook leaves that site alone. Because the exemption lives in the diff, it shows up in review instead of hiding.\n\nThis is the idea worth stealing even if you take nothing else from the repo.\n\nMost projects treat lint as a CI gate: the agent writes a whole feature, opens a PR, CI goes red, and by then the agent has moved on to something else. The correction is expensive and out of context.\n\nagent-starter runs lint on every single edit. `lint-on-edit.sh`\n\nruns `biome check --write`\n\nthen `eslint --fix`\n\non *just the file the agent touched*, and pipes any remaining errors back on stderr. The agent reads them on its next turn and fixes them while the context is still warm. Python gets the same treatment with `ruff check`\n\nthen `ruff format`\n\n.\n\nThe ruleset is tuned for the mistakes agents actually make, not for taste:\n\n`no-floating-promises`\n\n, `no-misused-promises`\n\n, `await-thenable`\n\n. That's the dropped-`await`\n\nclass, caught at write time.`import/no-unresolved`\n\nflags an import from a package that doesn't exist; `import/no-extraneous-dependencies`\n\nflags one that isn't declared.`no-explicit-any`\n\nplus the `no-unsafe-*`\n\nfamily`as any`\n\nescape hatch, so the agent has to fix the type shape instead of routing around it.`require-description`\n\n), so an `eslint-disable`\n\ncan't quietly become the path of least resistance.And a deliberate omission: no `max-lines`\n\n. Line caps punish coherent long code (a reducer, a parser, a JSX-heavy component) and reward the agent for shredding logic into single-use helpers that only make sense read together. Cognitive complexity, capped at 15, measures the thing you actually care about.\n\nBiome owns formatting and the fast syntactic rules (it's 10 to 100 times faster than ESLint); ESLint owns the type-aware rules that need the compiler. You get the speed on every keystroke and the depth where it counts.\n\n`check-file-size.sh`\n\nwarns when a file crosses a size target, since oversized files are where agents lose the thread. `check-codebase-health.sh`\n\nruns at session start so the agent opens with context instead of discovering problems mid-task. There's also an opt-in read-before-edit guard (`./install.sh --with-read-guard`\n\n) for older setups; recent Claude Code enforces read-before-edit natively, so it's left out of the default install on purpose.\n\nGuardrails stop bad writes. Context prevents them. The `CLAUDE.md`\n\ntemplate ships a 4-type memory system so what the agent knows about your project is structured, not a scratchpad:\n\n| Type | Holds |\n|---|---|\nuser |\nwho you are: role, preferences, level |\nfeedback |\nhow to work: corrections and confirmations, with the reason why |\nproject |\nwhat's happening: ongoing work, decisions, deadlines |\nreference |\nwhere to look: pointers to Linear, Grafana, Slack |\n\nTwo rules keep it honest. Never store what's derivable from code or git, because that copy goes stale and starts lying. And convert relative dates to absolute, because \"last week\" is meaningless three sessions later. `feedback`\n\nmemories carry a *Why* and a *How to apply*, so a correction you give once keeps applying itself.\n\nEverything above is a good frozen setup. The reason I built this instead of copying a pile of dotfiles is the loop on top.\n\nMost starters are a snapshot. Every project begins from the same rules and never gets smarter about how *this* codebase is actually used. agent-starter captures that signal and turns it into better rules. Four steps:\n\n```\n ① signal → ② store → ③ promote → ④ measure → (back to ①)\n   ▲                                              │\n   └──────────────────────────────────────────────┘\n```\n\n`await`\n\n, swallowed error, dangerous command), it appends one JSON line to `.harness/ledger.jsonl`\n\n. Logging is best-effort and always exits 0, so it can never break the hook that called it. Your explicit corrections are already captured as `feedback`\n\nmemories, which are the highest-value signal of all.`/reflect`\n\nskill reads the ledger and your feedback memories, clusters the recurring mistakes, and `recurring_events`\n\nmetric, and each reflection snapshots it to `.harness/reflections/`\n\n. The next reflection can check whether a rule you added last time actually reduced the mistake it targeted, so the loop closes instead of just piling up well-meaning rules nobody validated.The principle underneath it: **signal is private (the gitignored ledger), wisdom is shared (the committed reflections and the rules they produce).** A project scaffolded with `/new-project`\n\nis born with this wired in.\n\nThe base patterns aren't invented. A lot of them are reverse-engineered from the Claude Code CLI's own source: the file-size targets, the tool-per-directory layout, the lint tiering. Those are marked *\"derived from Anthropic's Claude Code source\"* in the repo. The self-improvement loop and the extra tooling are added on top.\n\nPick the entry point that matches what you want:\n\n`npx skills add sneg55/agent-starter -a claude-code -g`\n\n`~/.claude`\n\n:`./install.sh`\n\n. It merges its settings wiring idempotently, so re-runs never duplicate entries.Repo: [github.com/sneg55/agent-starter](https://github.com/sneg55/agent-starter), MIT licensed.\n\nIf you already work with an AI agent every day, the single highest-leverage piece is `lint-on-edit`\n\n. Move your linter out of CI and into the agent's write loop, and watch how much never reaches review in the first place.", "url": "https://wpnews.pro/news/make-your-codebase-safe-for-ai-agents-in-one-command", "canonical_source": "https://dev.to/sneg55/make-your-codebase-safe-for-ai-agents-in-one-command-854", "published_at": "2026-07-18 22:23:57+00:00", "updated_at": "2026-07-18 22:57:19.010524+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-safety", "large-language-models"], "entities": ["agent-starter", "Claude Code", "Biome", "ESLint", "Ruff"], "alternates": {"html": "https://wpnews.pro/news/make-your-codebase-safe-for-ai-agents-in-one-command", "markdown": "https://wpnews.pro/news/make-your-codebase-safe-for-ai-agents-in-one-command.md", "text": "https://wpnews.pro/news/make-your-codebase-safe-for-ai-agents-in-one-command.txt", "jsonld": "https://wpnews.pro/news/make-your-codebase-safe-for-ai-agents-in-one-command.jsonld"}}