Train your AI to be a good bot.
Auto-generate guardrail files that keep Claude, Cursor, Windsurf, Codex and Copilot aligned with your project's conventions — then detect when they go stale.
npx goodbot-ai init && npx goodbot-ai generate
AI agents are powerful but they don't know your rules. Without guardrails, they create god files, mix business logic into UI components, bypass your layer boundaries, and drift across agents (your CLAUDE.md says one thing, your .cursorrules says another).
goodbot solves three problems:
- Generate once, use everywhere. One source of truth →
CLAUDE.md,.cursorrules,.windsurfrules,AGENTS.md,CODING_GUIDELINES.md. - Make the rules adaptive. Scans your actual codebase for framework conventions, architectural layers, and real violations — so guardrails match your code, not a generic template.
- Detect when they go stale. As your codebase evolves, goodbot tracks drift between what the guardrails claim and what the code actually looks like.
New here? Read HOW_IT_WORKS.md for a plain-English walkthrough with a diagram of the full pipeline — what goodbot reads from your project, what it writes, and how drift detection works.
npx goodbot-ai init # interactive, or add --preset recommended
npx goodbot-ai generate # writes CLAUDE.md, CODING_GUIDELINES.md, etc.That's it. On first run goodbot scans your project, runs an architectural analysis, detects your framework's conventions (e.g. NestJS modules, React hooks), and generates guardrail files tailored to what it found. It saves an analysis snapshot so future runs can show you what's changed.
Skip the prompts with a preset (safe for CI):
npx goodbot-ai init --preset recommended --on-conflict merge| Preset | What you get |
|---|---|
strict |
Barrel imports enforced, interface contracts, all agent files, tight thresholds |
recommended |
Balanced defaults — barrel imports recommended, all agent files |
relaxed |
Minimal guardrails — just the basic generated files |
Run goodbot presets to see a side-by-side comparison.
goodbot is designed to fit into your team's workflow at four moments:
npx goodbot-ai init --preset recommended
npx goodbot-ai generate
git add CLAUDE.md CODING_GUIDELINES.md .cursorrules .windsurfrules AGENTS.md .cursorignore .goodbot/config.json .goodbot/.gitignore
git commit -m "Add AI agent guardrails"goodbot detects whether you're building an API, a UI, a full-stack app, or a library, and applies the right stability ordering (Stable Dependency Principle) for that system type. For a NestJS API: controllers depend on services depend on repositories depend on domain. For a React app: screens depend on hooks depend on services depend on types.
Your team's AI agents now have a shared understanding of:
- Which layers exist and their ordering
- Where business logic belongs vs where it doesn't
- Framework-specific red flags (e.g. "business logic in NestJS controllers")
- SOLID and design principles framed for your specific framework
- Current architectural health (grade + top issues)
Safe re-runs: if CLAUDE.md (or any agent file) already exists, goodbot wraps its content in
<!-- goodbot:start/end -->markers and prepends at the top — your team notes below are preserved.
No action needed. Your agents read the generated files automatically. If you tweak a rule (e.g. add a line to conventions.customRules in .goodbot/config.json), re-run generate:
npx goodbot-ai generate # fast — reuses cached snapshotQuick re-runs reuse the last analysis snapshot so the Current Health block still renders without a full scan. Pass --analyze to refresh the analysis:
npx goodbot-ai generate --analyzeInstall git hooks locally, add a CI step, and goodbot catches new violations before they merge:
# Local — once per dev machine
npx goodbot-ai hooks install# .github/workflows/goodbot.yml
on: [pull_request]
jobs:
guardrails:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx goodbot-ai check --strict # drift + stale suppressions
- run: npx goodbot-ai freshness # health claims still accurate?
- run: npx goodbot-ai diff --freshness # did THIS PR move the grade?Or use the official GitHub Action for PR comments with emoji bars:
- uses: timeritual/goodbot-ai@main
with:
mode: diff
comment: 'true'
fail-on-grade: COnce a month (or when the analysis snapshot is 7+ days old), run:
npx goodbot-ai freshness # what's stale? what's degraded?
npx goodbot-ai generate --analyze --force # refresh guardrailsfreshness compares the snapshot goodbot saved at generation time against the current state of the codebase — and tells you exactly which claims have drifted:
Guardrail Freshness Report (generated 12 days ago)
───────────────────────────────────────────────────────
Health grade A → B+ ⚠ stale
Circular dependencies 0 → 2 ✗ degraded (+2)
Dead exports 0 → 3 ✗ degraded (+3)
Barrel violations 5 → 3 ↑ improved (-2)
✗ Your guardrails are stale. Run `goodbot generate --analyze --force` to update.
Framework upgrade? Added a new top-level directory? Run init again:
npx goodbot-ai init --preset recommendedYour customizations are preserved. goodbot refreshes only the scan-detected fields (framework, layers, system type) and keeps everything you've hand-edited (verification commands with custom flags, custom rules, suppressions, thresholds, budgets). A diff of what changed prints before the save. Pass --force only if you want to blow everything away.
| File | Read by | Contents |
|---|---|---|
CODING_GUIDELINES.md |
All agents + humans | Single source of truth — architecture diagram, SOLID + design principles, business logic rules, detected framework conventions, "Known Issues" list, verification checklist |
CLAUDE.md |
Claude Code, Claude in IDEs | Quick reference + current health snapshot, points to CODING_GUIDELINES.md |
.cursorrules / .windsurfrules / AGENTS.md |
Cursor / Windsurf / Codex | Point to CODING_GUIDELINES.md — eliminates cross-agent drift |
.cursorignore |
Cursor | Keeps build artifacts, secrets, noise out of AI context |
Plus internal state in .goodbot/:
| File | Commit? |
|---|---|
.goodbot/config.json |
Yes — your shared team config |
.goodbot/.gitignore |
Yes — auto-created, ignores the rest |
.goodbot/snapshot.json, checksums.json, history.json |
No — local analysis state |
| Command | What it does |
|---|---|
goodbot init |
Set up .goodbot/config.json. Merges into existing config by default (preserves customizations); --force overwrites. |
goodbot generate |
Write/refresh the guardrail files. Safe to re-run — preserves user content via markers. |
goodbot check |
Verify generated files haven't drifted. --strict also fails on stale suppressions. CI-friendly (exit 1). |
goodbot freshness |
Compare snapshot claims to current reality. Tells you what's stale. |
goodbot analyze |
Full architectural audit — dependency graph, SOLID, layer violations, health grade. |
goodbot diff |
Show violations introduced by the current branch (vs base). Great for PR review. |
goodbot hooks install |
Install git hooks that warn on stale guardrails. |
goodbot suppress |
List detected violations with content-based IDs; emit paste-ready suppression JSON. --apply --reason "..." writes to config. |
goodbot unsuppress <id> |
Remove a suppression from analysis.suppressions by its content-based ID. |
goodbot score |
One-line health grade. Fast enough for pre-commit hooks. |
goodbot presets |
Side-by-side comparison of the strict/recommended/relaxed presets. |
Run any command with --help for full flags. Other commands available but less frequently used: scan, watch, fix, pr, onboard, trend, sync, report, ci.
goodbot auto-detects your framework and applies a canonical layer ordering + framework-specific conventions.
| Framework | System type | Canonical layers |
|---|---|---|
| React / React Native | UI | types → utils → api → services → state → hooks → components → screens → navigation |
| Angular | UI | types → services → pipes → directives → interceptors → guards → components → NgModules → pages |
| Vue | UI | types → services → stores → composables → plugins → components → layouts → pages → router |
| Next.js / Nuxt | Mixed | Combines API and UI layers — server routes at low level, pages at top |
| NestJS | API | domain → config → repositories → infrastructure → services → modules → cross-cutting → controllers |
| Express / Node | API | domain → config → services → routes/middleware → controllers |
Plus reasonable defaults for Django, Flask, FastAPI, and Go (framework detection works; deep analysis is TypeScript/JavaScript only for now).
For NestJS specifically, goodbot also detects decorator-based patterns (*.controller.ts, *.module.ts, *.entity.ts, guards, interceptors, pipes) and surfaces them in CODING_GUIDELINES.md so agents follow your project's real conventions.
Unknown framework? goodbot treats it as a library and still produces sensible guardrails.
All config lives in .goodbot/config.json. It's meant to be committed and shared across the team. The key sections:
Old configs with ignore.paths, analysis.ignore, or plural circularDeps keys still load — goodbot auto-migrates them and prints a deprecation warning. Re-save the config to persist the canonical shape.
Edit directly or re-run goodbot init to regenerate (merges by default, preserves your edits).
Four knobs, pick based on intent:
| Knob | Scope | When to use |
|---|---|---|
analysis.suppressions |
Exact file/cycle + rule + required reason | You accept this specific violation intentionally. Shows as (N suppressed) in output. |
analysis.exclude.* |
Glob pattern per check category | Well-known false positives (TypeORM entity cycles, generated code). Doesn't appear at all. |
.goodbot/ignore |
File paths → all checks | Legacy / vendored code you never want analyzed. |
| Violation budgets | Per-category threshold | Known debt you want visible but not failing CI until it grows past a limit. |
ignore.paths in config only affects .cursorignore output — it does NOT suppress analysis. Use one of the four knobs above.
Use goodbot suppress to add suppressions safely. IDs are content-based and stable — the ID for a cycle stays the same while the cycle exists, and disappears when you fix it. No more hand-typing Unicode ↔ or cycle patterns.
$ goodbot suppress
[cycle-app-database] cycle: app → database → app
[layer-src-scripts-migrate] src/scripts/migrate.ts (L4 → L5)
[oversized-src-generated-schema] src/generated/schema.ts: File has 6000 lines
$ goodbot suppress cycle-app-database --reason "Bootstrap wiring" --apply
✓ Added suppression to .goodbot/config.json
$ goodbot unsuppress cycle-app-database
✓ Removed suppression: circularDep cycle="database ↔ app"--apply requires a real --reason (min 8 chars, no literal TODO: placeholders) so nothing undocumented reaches main.
Goodbot also warns loudly on every analyze/generate about any suppression that matches no detected violation — so typos / stale entries can't silently disable guardrails. If a CI script references cycle-app-database and the cycle is fixed, the next run errors clearly ("No violation with that id") instead of silently suppressing the wrong thing.
analysis.ignorerenamed toanalysis.exclude. Disambiguates fromoutput.cursorignore— the word "ignore" now only appears in the filename-scoped cursorignore. Oldanalysis.ignorestill loads, auto-migrates on save with a deprecation warning.
goodbot check --strictfor CI enforcement. Orphaned suppressions (entries that no longer match any detected violation) previously only printed a warning — CI wouldn't catch them. Nowgoodbot check --strictruns the analysis, verifies every suppression still targets a real violation, and exits 1 if any are orphaned. Add it to your CI pipeline so stale suppressions can't silently reachmain.
goodbot unsuppress <id>closes the loop. Adding a suppression was one command; removing one required hand-editing JSON. Now both use the same content-based IDs —goodbot unsuppress cycle-app-databaseremoves the matching entry cleanly.--applyrequires a real--reason. Empty, literalTODO:placeholders, and too-short (<8 chars) reasons are rejected before writing to config. No more "TODO: explain why..." placeholders reaching main.- Husky v9 hooks install fix.
goodbot hooks installnow correctly installs into.husky/(user-facing) when husky v9 is detected, instead of.husky/_/(husky's internal regenerated dir). Previously goodbot's hooks were wiped silently on the nextnpm install.
- Consistent singular rule names.
analysis.ignore.*keys now matchanalysis.suppressions[].rule— both use singular (circularDep,layerViolation,oversizedFile). Old plural keys still work and are auto-migrated on load with a deprecation warning. output.cursorignorereplaces top-levelignore. The oldignore.pathsfield (which only affected.cursorignore, not analysis — a frequent source of confusion) is nowoutput.cursorignore.paths. Auto-migrated on load.- Stable content-based suppression IDs. The IDs emitted by
goodbot suppressare now content-based and invariant (e.g.cycle-app-database,layer-src-scripts-migrate,oversized-src-user-service) instead of positional (c0,l0). As long as the violation exists, the ID stays the same. When you fix it, the ID simply disappears — CI scripts that reference a fixed violation's ID fail loudly instead of silently suppressing the wrong thing.
- Orphaned-suppression warnings. Suppressions that match no detected violation (typo in cycle pattern, renamed/deleted file, already-fixed bug) are flagged loudly on every
analyzeandgenerate. No more silently-dead suppressions lurking in config. goodbot suppresscommand. Lists suppressible violations with IDs and emits paste-ready JSON.goodbot suppress <id> --reason "..." --applywrites to config for you. No need to hand-type the Unicode↔character anymore.- Broader cycle syntax.
analysis.suppressions[*].cyclenow accepts all common forms:a → b,a ↔ b,a -> b,a <-> b,a,b,a > b, and the human-natural loop forma → b → a. All normalize to the same canonical cycle.
- Re-init preserves customizations.
goodbot init --preset recommendedno longer clobbers hand-edited fields. Refreshes scan-detected fields (framework, layers), preserves everything else.--forcefor full overwrite. - Per-rule suppressions with reasons. Accept a specific violation intentionally via
analysis.suppressions. Each entry needs arule + file(orrule + cycle) and areason. Shows as(N suppressed)in output. - Next-step prompts. After
initandgenerate, the CLI surfaces the highest-value next actions (generate, install hooks, check freshness, add to CI) instead of a single-line hint. - Cached snapshot reuse. Quick
generatere-runs render the Current Health block from the last snapshot without re-scanning — pass--analyzeto refresh.
Works with anything that reads one of the standard guardrail files:
- Claude Code, Claude in Cursor / VS Code / IDE extensions — via
CLAUDE.md - Cursor — via
.cursorrules+.cursorignore - Windsurf — via
.windsurfrules - OpenAI Codex — via
AGENTS.md - GitHub Copilot / any other agent — via
CODING_GUIDELINES.md(the single source of truth all other files point to)
I already have a CLAUDE.md with my team's notes — will goodbot overwrite it?
No. Goodbot wraps its content between <!-- goodbot:start --> / <!-- goodbot:end --> markers and prepends at the top. Your content below the markers is preserved across every re-run. On regeneration, only the marker section is replaced. You can change this via agentFiles.existingFileStrategy in config (merge / overwrite / skip) or pass --on-conflict to init.
Running init a second time wiped my custom typecheck command.
It shouldn't, as of 0.8.0. Re-running init now merges the preset into your existing config — only scan-detected fields (framework, layers, systemType) get refreshed, everything you've hand-edited (verification commands, custom rules, suppressions, thresholds) is preserved. If you want a full reset, pass --force. If you're on an older version, upgrade: npm install -g goodbot-ai@latest.
My CI passes but a stale suppression is hiding a real violation.
Add --strict to goodbot check in CI — it fails when any analysis.suppressions entry no longer matches a real detected violation, so stale entries can't silently disable guardrails.
- run: npx goodbot-ai check --strictLocally, goodbot suppress (no args) lists current violations with IDs, and goodbot analyze flags orphaned suppressions loudly in stderr.
Circular dependency count is inflated by ORM entity relationships (TypeORM, Mongoose).
The recommended preset for NestJS / Express / Node / FastAPI / Django / Flask automatically adds analysis.exclude.circularDep: ["**/entities/**", "**/models/**", "**/schemas/**"], which drops cycles whose files all match the pattern. If you have entities in a different directory, add your path to that list.
For a specific cycle you want to accept (with a reason), use goodbot suppress cycle-<modules>.
Goodbot installed git hooks but npm install seems to overwrite them.
Fixed in 0.11.0. Husky v9 sets core.hooksPath=.husky/_ (husky's internal dir, regenerated on every install). Goodbot now detects this and installs into .husky/ (user-facing) instead, so changes survive husky reinstalls. Upgrade to 0.11.0+ if you're hitting this.
The freshness report says "stale" even though my changes improved the codebase.
"Stale" means "your snapshot no longer reflects reality" — regardless of direction. Improvements are tracked too (look for ↑ improved rows in the report). After major improvements, re-run goodbot generate --analyze --force to refresh the snapshot so the baseline reflects your new state.
Main branch detected as "main" but ours is "development".
As of 0.6.3, goodbot detects the default branch via (in order): local origin/HEAD → git ls-remote --symref origin HEAD (authoritative network call, 5s timeout) → commit-count heuristic across main/master/development/develop/trunk. If ls-remote fails (e.g. private repo without credentials in the CLI context), it falls back to the branch with the most commits — which for GitFlow-style repos correctly picks development.
You can always edit conventions.mainBranch directly in .goodbot/config.json — init will preserve that on re-runs.
AI coding agents are like eager interns — fast, capable, and in desperate need of clear rules. goodbot is obedience training for your AI: set the rules once, enforce them everywhere, catch drift before it ships.
git clone https://github.com/timeritual/goodbot-ai.git
cd goodbot-ai
npm install
npx tsx src/index.ts --help # run in dev modeTwo docs to orient:
- HOW_IT_WORKS.md — end-user mental model (what goodbot does, what state it keeps, how drift detection works).
- ARCHITECTURE.md — contributor map (subsystem layout, key design decisions, extension points).
Issues and PRs welcome.
MIT
Built by timeritual
{ "project": { "name": "...", "framework": "...", "language": "..." }, "architecture": { "systemType": "api", "layers": [...], "barrelImportRule": "recommended" }, "businessLogic": { "allowedIn": ["services"], "forbiddenIn": ["controllers", "guards"] }, "verification": { "typecheck": "npm run typecheck", "lint": "npm run lint", "test": "npm test" }, "conventions": { "mainBranch": "main", "customRules": ["Project-specific rule"] }, "analysis": { "thresholds": { "maxFileLines": 300, "maxBarrelExports": 15 }, "budget": { "circular": 0, "srp": 10 }, "exclude": { "circularDep": ["**/entities/**"] }, // analysis-scoped exclusions — singular keys match `suppressions[].rule` "suppressions": [{ "rule": "layerViolation", "file": "src/scripts/migrate.ts", "reason": "..." }] }, "output": { "cursorignore": { "paths": ["dist", "build"], "sensitiveFiles": [".env"] } } }