Tech Stacks
The reviewer detects your project's tech stack automatically from package.json, pubspec.yaml, composer.json, and other marker files. It then loads a built-in rules template optimized for that stack.
Auto-detection order
Detection is ordered from most specific to most generic so that, for example, a Next.js app gets the Next.js rules and not just the React ones.
| Order | Stack | Detected by |
|---|---|---|
| 1 | NestJS | @nestjs/core in dependencies |
| 2 | Next.js | next in dependencies |
| 3 | React | react in dependencies |
| 4 | Flutter | pubspec.yaml present |
| 5 | Laravel | composer.json with laravel/framework |
| 6 | Node.js | package.json present (no framework match) |
| 7 | TypeScript | tsconfig.json present |
| 8 | Generic | Fallback |
Force a stack
Override auto-detection with tech: in .ai-review.yml:
tech: nestjsValid values: nestjs | nextjs | react | typescript | node | flutter | laravel | generic
Monorepos (multiple stacks)
If your app's package.json/pubspec.yaml/composer.json isn't at the repo root, set appDir in .ai-review.yml so detection looks in the right place:
appDir: apps/webWhen a monorepo has multiple subprojects with independent stacks (e.g. a Flutter app and a NestJS backend in the same PR), pass a list instead of a single string:
appDir:
- apps/web # Flutter
- apps/api # NestJS
- packages/shared_typesEach changed file is reviewed with the rules of whichever configured directory is its longest matching path prefix — a file under apps/web/lib/ gets Flutter rules, a file under apps/api/src/ gets NestJS rules. Files outside every configured directory (a root README.md, a workflow YAML) fall back to the stack detected at the repo root.
Under the hood, one LLM call runs per detected stack, and the results are merged into a single PR review: the worst recommendation across groups wins (a major finding in any one subproject forces REQUEST_CHANGES for the whole PR — see Auto-Approve), and the summary is split into a section per group.
To bound cost on repos with many subprojects, only the largest maxStackGroups groups (default 4) get their own LLM call — extras are folded into the root/fallback group instead of growing the call count without limit:
maxStackGroups: 4Stack-specific rules
NestJS (nestjs)
- Dependency injection: services, repositories, and providers must be injected via constructor — no direct instantiation inside methods
- Decorators: guard, interceptor, and pipe usage;
@UseGuards,@UseInterceptors,@UsePipeson controllers and methods - Module boundaries: imports, exports, and providers declared correctly per module
- DTOs: input validation with
class-validator; no raw request body access - Exception handling:
HttpExceptionand built-in exception filters; no rawthrow new Error() - Database: TypeORM repository pattern; avoid direct
EntityManagercalls for complex queries - Security: guards on authenticated routes; no
@Public()on sensitive endpoints
Next.js (nextjs)
- Component model: correct use of Server Components vs Client Components;
use clientonly where necessary - Data fetching:
fetchwith appropriate caching (cache: 'no-store'for dynamic,revalidatefor ISR) - Routing: App Router conventions (layout, page, loading, error files); no mixing Pages Router patterns
- Image optimization:
next/imageinstead of<img>tags - Link navigation:
next/linkinstead of<a>for internal routes - Environment:
NEXT_PUBLIC_prefix for client-exposed vars; no secrets in client bundles - Performance: avoid large client-side bundles; lazy load heavy components
React (react)
- Hooks: rules of hooks (no conditional or nested calls); dependency arrays complete and accurate
- State: minimal state; derived values computed, not stored
- Performance:
useCallback/useMemowhen genuinely needed; avoid premature memoization - Prop drilling: context or state library for deeply shared state
- Side effects:
useEffectcleanup; no async directly inuseEffect - Keys: stable, unique keys in lists; no array index keys for mutable lists
TypeScript (typescript)
- Types: no
any; explicit return types on exported functions - Null safety: explicit
undefined/nullhandling; use optional chaining and nullish coalescing - Generics: constrained generics; no unconstrained
<T> - Enums:
const enumfor closed value sets; union types for open-ended discriminants - Assertions: no
as Typecasts except in test fixtures or type guards
Flutter (flutter)
- State management: BLoC/Cubit pattern; no direct
setStatein large widgets - Widget separation: UI-only widgets; no business logic in
build()methods - Async:
FutureBuilder/StreamBuilderusage;async/awaitin controllers - Navigation: named routes or GoRouter; no direct
MaterialPageRoutechains - Platform:
Platform.isAndroid/Platform.isIOSguards for platform-specific code - Performance:
constconstructors;ListView.builderfor long lists
Laravel (laravel)
- ORM: Eloquent relationships correctly defined;
with()for eager loading to avoid N+1 - Validation:
FormRequestclasses; no inline validation in controllers - Auth:
auth()->user()access; policy-based authorization; no raw user ID from request - Migrations: reversible
down()methods; no schema changes in seeder files - Routes: RESTful resource controllers; route model binding
- Security: CSRF on POST/PUT/DELETE; no unparameterized raw SQL queries
Node.js (node)
- Async:
async/awaitover callbacks; proper error propagation (try/catch) - Event loop: no synchronous file/network I/O in hot paths
- Environment:
process.envaccess behind a config module; noprocess.env.Xscattered - Error handling: unhandled promise rejections;
process.on('unhandledRejection') - Security:
helmetfor HTTP headers; input sanitization; avoid dynamic code execution
Generic (generic)
Applies to any stack. Covers:
- Security: injection, XSS, hardcoded credentials
- Performance: algorithmic complexity, unnecessary loops
- Maintainability: complexity, naming, duplication
- Bug risk: null safety, off-by-one, race conditions
- Architecture: separation of concerns, dependency direction
Combining with custom rules
Built-in template rules are always loaded first. Your code-review-rules.md (or rules: file in config) is appended after, so your rules win on conflicts.
# .ai-review.yml
tech: nestjs
rules: ./team-rules.mdSee Custom Rules for examples.