AI coding assistants will cheerfully invent a fourth state library, import across package boundaries, and “fix” a bug by silencing TypeScript. In a monorepo that is expensive. Cursor rules (project rules / .cursor/rules / AGENTS.md-style guidance the agent actually reads) are how you turn tribal knowledge into machine-enforceable defaults: package boundaries, test commands, API patterns, and “never do this” lists. The unfair advantage is not a longer prompt — it is rules that match CI, so edits survive review.
⚡ TL;DR: Put durable engineering law in versioned Cursor project rules scoped by glob (apps vs packages vs infra). Encode package boundaries (
no cross-imports except via public barrels), required commands (pnpm lint,pnpm test -w @scope/foo), and architectural do/don’ts. Keep secrets out of rules; point agents at runbooks. Pair rules with CI identical to what you tell the agent. Illustrative win: cut “agent PR thrash” cycles by making the first patch already passtsc -band package tests.
Why monorepos punish unconstrained agents
A single-repo Node app can absorb messy imports. A pnpm/turbo/nx workspace cannot:
- Agents add dependencies to the wrong
package.json - They reach into
packages/ui/src/internalinstead of the public export map - They duplicate types already in
packages/contracts - They “fix” CI by widening
anyor disabling eslint on a line
Cursor rules shrink the search space. Treat them like lint for the agent’s planning.
<!-- .cursor/rules/monorepo-core.mdc (illustrative shape) -->
---
description: Core TypeScript monorepo laws for CheatCoders-style workspaces
globs: ["**/*.{ts,tsx}"]
alwaysApply: true
---
# Monorepo core
- Package manager: **pnpm** workspaces. Never suggest npm/yarn install at repo root.
- TypeScript project references: run `pnpm exec tsc -b` after cross-package edits.
- Imports: only via package name (`@acme/ui`) or relative within the same package.
- ❌ No deep imports into another package’s `src/`.
- ✅ Add deps with `pnpm add <pkg> --filter @acme/<name>`.
- Prefer editing existing abstractions over inventing parallel utilities.
✅ Rules that name commands that exist in package.json.
❌ Vague “write clean code” slogans the model already heard a thousand times.
Scope rules by glob: apps, packages, infra
One giant rule file becomes noise. Split by blast radius:
.cursor/rules/
00-monorepo-core.mdc # alwaysApply
10-packages-ui.mdc # globs: packages/ui/**
20-apps-web.mdc # globs: apps/web/**
30-functions-aws.mdc # globs: packages/functions/**, infra/**
40-testing.mdc # globs: **/*.{test,spec}.ts*
---
description: AWS Lambda package conventions
globs: ["packages/functions/**", "infra/**"]
---
# Lambda packages
- Runtime: Node 20, ARM64 preferred when deps allow.
- Observability: use Lambda Powertools Logger/Metrics/Tracer patterns already in `packages/functions/src/observability.ts`.
- ❌ Don’t log Authorization headers or raw PII.
- ✅ Idempotency for SQS consumers (see internal doc link / sibling package).
- Bundling: esbuild via the existing `pnpm --filter @acme/functions build` pipeline — don’t invent a second bundler.
- Timeouts: set SDK client timeouts under function timeout; see production runbook.
That AWS-flavored rule is optional but natural when the monorepo ships Lambdas — align with LLM Coding Agents on AWS: Safe Tool Sandboxes with Lambda and AI Code Review Bots: IAM, Secrets, and Least-Privilege Pipelines so local agent edits do not fight your PR bot.
Encode boundaries in both rules and tooling
Rules without enforcement are fanfic. Mirror them in ESLint/dependency-cruiser/nx tags:
// eslint.config.js — illustrative boundary (eslint-plugin-import or @nx/enforce-module-boundaries)
export default [
{
files: ["packages/ui/**/*.{ts,tsx}"],
rules: {
"no-restricted-imports": [
"error",
{
patterns: [
{
group: ["@acme/api/*", "**/apps/**"],
message: "UI cannot import apps or API internals. Use @acme/contracts.",
},
],
},
],
},
},
];
// packages/ui/package.json — exports map is the public API contract
{
"name": "@acme/ui",
"exports": {
".": "./dist/index.js",
"./styles.css": "./dist/styles.css"
},
"types": "./dist/index.d.ts"
}
Tell Cursor explicitly:
# Public API
- When adding a UI component, export it from `packages/ui/src/index.ts` and rebuild types.
- Consumers must import `@acme/ui`, never `@acme/ui/src/Button`.
- If you need a new cross-package type, put it in `@acme/contracts` first.
Make “done” mean green: commands in the rule
Agents stop early when “done” is undefined. Put the acceptance commands in the rule file:
## Definition of done for TS changes
1. `pnpm exec prettier --write` on touched files (or project format script).
2. `pnpm exec tsc -b --pretty false` at repo root — zero errors.
3. Tests: `pnpm --filter <affected> test` for every package you changed.
4. If you touched `packages/functions`, run the package’s `test:unit` and do **not** deploy from the agent.
5. Summarize: packages touched, commands run, residual risks.
Optional helper script the agent can call:
// tools/affected-check.ts — thin wrapper you maintain; agent runs it
import { execSync } from "node:child_process";
const filters = process.argv.slice(2);
if (!filters.length) {
console.error("usage: affected-check @acme/ui @acme/web");
process.exit(2);
}
for (const f of filters) {
console.log(`\n=== ${f} ===`);
execSync(`pnpm --filter ${f} lint`, { stdio: "inherit" });
execSync(`pnpm --filter ${f} test`, { stdio: "inherit" });
}
execSync("pnpm exec tsc -b", { stdio: "inherit" });
✅ Agent runs your script.
❌ Agent improvises npx jest with a different config.
Prompt patterns that stick (without fighting the rules)
In chat, still be specific — rules are defaults, not mind readers:
Implement rate-limit headers on apps/web API routes.
Follow .cursor rules. Only touch apps/web and @acme/contracts if types change.
Run the definition-of-done commands before summarizing.
Do not add new dependencies unless required; prefer existing middleware.
For refactors, forbid scope creep:
Move `formatMoney` from apps/web/lib to @acme/contracts.
Update imports. No behavior change. No new abstractions.
When the agent proposes AWS changes, point it at existing IAM least-privilege patterns from AI Code Review Bots: IAM, Secrets, and Least-Privilege Pipelines and sandboxing from LLM Coding Agents on AWS — do not let it mint *:* “so the demo works.”
Keep rules maintainable
- Version rules with the repo — PR review for rule changes like any other policy.
- Delete stale rules — wrong guidance is worse than none.
- Never put secrets / prod URLs with tokens in rules.
- Link to canonical docs instead of pasting 2k-line style guides.
- Rehearse quarterly: break a boundary on purpose; confirm eslint + agent both catch it.
## Anti-patterns for this repo
- ❌ Disabling eslint with wide `any` to silence TS
- ❌ Adding Redux/Zustand/Jotai when React Query + context already covers the case
- ❌ Creating `utils2.ts` instead of extending existing helpers
- ❌ Editing generated code under `**/generated/**`
- ✅ Prefer small PRs: one package’s public API change + follow-up consumer PR if needed
Bedrock/tool agents in your cloud account still need runtime guardrails (Bedrock Agents: Tool Use, Memory, and Production Guardrails); Cursor rules are the dev-time cousin — complementary, not interchangeable.
Closing checklist
✅ Dos
– ✅ Version .cursor/rules with globs and alwaysApply core laws
– ✅ Mirror boundaries in eslint/exports/CI
– ✅ Spell out exact pnpm/tsc/test commands as definition of done
– ✅ Scope AWS/Lambda rules to those packages only
– ✅ Review rule diffs like policy changes
❌ Don’ts
– ❌ Don’t paste secrets into rules or examples
– ❌ Don’t write novel-length rules nobody updates
– ❌ Don’t allow deep imports the linter forbids
– ❌ Don’t let agents invent second build pipelines
– ❌ Don’t skip running the same checks CI runs
Related reading
- LLM Coding Agents on AWS: Safe Tool Sandboxes with Lambda
- AI Code Review Bots: IAM, Secrets, and Least-Privilege Pipelines
- Amazon Bedrock Agents: Tool Use, Memory, and Production Guardrails
- Express.js Best Practices: Build Production-Ready APIs With Node.js
- Lambda Powertools for Node: Structured Logs That Survive On-Call — what agents should reuse in functions packages
- Node.js Event Loop Lag: Catch p99 Stalls Before Users Feel Them
Last updated on September 10, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.

Pingback: Structured Outputs for Codegen: JSON Schemas That Actually Compile - CheatCoders
Pingback: Claude Code Hooks: Gate Risky Shell Commands Before CI Runs - CheatCoders