Production-grade rules files for Cursor and Claude Code. Drop them in your project. Your AI follows real architecture patterns instead of making it up.
AI writes code that runs. But it doesn't think about what happens when your app needs to change, scale, or survive a security audit.
500+ line files that handle routes, database calls, business logic, and auth. One wrong edit breaks everything.
Prisma imported in 47 different files. Need to add caching? Good luck finding them all.
catch (e) { console.log(e) }. Error swallowed, user sees a blank screen, you see nothing in production.
findMany() with no limit. Works with 10 test users. Crashes with 10,000 real ones.
API keys right in the source code. Pushed to GitHub. Compromised within minutes.
AI builds the happy path. It skips the middleware that checks if the user is actually allowed to be there.
4 complete stack configurations, each with a .cursorrules file and a CLAUDE.md project file.
The most common vibe-coded stack. Server Components, App Router, the works.
API and backend projects. Async patterns, SQLAlchemy, proper error handling.
Full-stack JavaScript. REST API patterns, middleware chains, state management.
Language-agnostic architecture rules. Works with any stack or language.
Every rule targets a specific AI anti-pattern with the exact fix.
Same prompt. Same AI. One project had ShipKit loaded.
const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const pool = require('../db');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) throw new Error('JWT_SECRET is required');
// Validation, business logic, DB query, and JWT all in one place
router.post('/register', async (req, res) => {
const { email, password } = req.body;
// Manual validation — repeated on every route
if (!email || !password)
return res.status(400).json({ error: 'Email and password are required' });
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
return res.status(400).json({ error: 'Invalid email address' });
if (password.length < 8)
return res.status(400).json({ error: 'Password must be at least 8 characters' });
try {
// DB query directly in the route — untestable in isolation
const existing = await pool.query(
'SELECT id FROM users WHERE email = $1', [email.toLowerCase()]
);
if (existing.rows.length > 0)
return res.status(409).json({ error: 'Email already registered' });
const passwordHash = await bcrypt.hash(password, 12);
const result = await pool.query(
'INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING *',
[email.toLowerCase(), passwordHash]
);
const user = result.rows[0];
const token = jwt.sign(
{ sub: user.id, email: user.email },
JWT_SECRET,
{ expiresIn: '7d' }
);
return res.status(201).json({ token, user: { id: user.id, email: user.email } });
} catch (err) {
// Generic — caller gets no useful info
console.error('Registration error:', err);
return res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = router;
const express = require('express');
const authRoutes = require('./routes/auth');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.use('/auth', authRoutes);
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
});
module.exports = pool;
500 error. Caller gets "Internal server error", useless for debugging.// Route just wires things together — nothing else
import { Router } from 'express';
import { z } from 'zod';
import { validate } from '../middleware/validate';
import { register } from '../controllers/auth.controller';
const router = Router();
const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
router.post('/register', validate(registerSchema), register);
export default router;
// Business logic only — testable without HTTP or a database
export class ConflictError extends Error {
constructor(message: string) {
super(message);
this.name = 'ConflictError';
}
}
export async function registerUser(input: RegisterInput): Promise<string> {
const existing = await findUserByEmail(input.email);
if (existing) throw new ConflictError('Email already in use');
const passwordHash = await bcrypt.hash(input.password, 12);
const user = await createUser(input.email, passwordHash);
return jwt.sign(
{ sub: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
);
}
// Data access only — swap Prisma for Drizzle/Kysely in one file
export async function findUserByEmail(email: string): Promise<User | null> {
return prisma.user.findUnique({ where: { email } });
}
export async function createUser(
email: string,
passwordHash: string
): Promise<User> {
return prisma.user.create({
data: { email, passwordHash },
});
}
ConflictError. Errors propagate cleanly. The controller catches exactly what it expects.No dependencies. No build step. Just files.
Choose from Next.js, Python, React/Node, or Universal.
Drop .cursorrules and CLAUDE.md into your repo.
Guardrails apply automatically. No config needed.
Cursor, Claude Code (claude.ai projects & CLI), and any AI coding tool that reads .cursorrules or project-level config files. The rules are plain text. They work everywhere.
Most cursorrules files are generic ("use TypeScript", "follow best practices"). ShipKit targets specific architectural anti-patterns that AI creates, with the exact mistake, why it breaks, and the correct fix. It's the difference between "eat healthy" and an actual meal plan.
No. Rules files add context, not latency. Your AI reads them once per session. If anything, it speeds you up: fewer rewrites, fewer bugs, less time debugging architecture problems later.
Absolutely. They're plain markdown and text files. Add rules, remove ones that don't apply, adjust to your codebase. That's the whole point. They're yours to own.
The Universal config works with any language or framework. The architecture principles (separation of concerns, data layer boundaries, error handling) apply everywhere.
$19. One-time purchase, instant download.