It's not you, it's your prompt.
Inline prompt enhancement for developers.
Free · No credit card required
The power of a better prompt
import express from "express";
import rateLimit from "express-rate-limit";
const app = express();
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 20,
message: "Too many requests",
});
app.use("/api/", limiter);
app.post("/api/submit", (req, res) => {
const { email, plan } = req.body;
// ... process submission
res.json({ success: true });
});import express from "express";
import { redis } from "./lib/redis";
import { RATE_WINDOW_SEC, RATE_MAX_HITS } from "./lib/config";
import type { NextFunction, Request, Response } from "express";
// brief picked up project defaults from lib/config.ts (60s / 20)
async function rateLimit(
req: Request, res: Response, next: NextFunction
) {
const key = `rl:submit:${req.ip}`;
const hits = await redis.incr(key);
if (hits === 1) await redis.expire(key, RATE_WINDOW_SEC);
if (hits > RATE_MAX_HITS) {
// match existing 429 shape from authRouter
return res.status(429).json({
error: "rate_limit_exceeded",
retryAfter: await redis.ttl(key),
});
}
next();
}
app.post("/api/submit", rateLimit, (req, res) => {
const { email, plan } = req.body;
// ... process submission
res.json({ success: true });
});
// --- test ---
describe("rateLimit", () => {
it("returns 429 after max hits", async () => {
for (let i = 0; i < 21; i++) {
const res = await request(app).post("/api/submit");
if (i < 20) expect(res.status).toBe(200);
else expect(res.status).toBe(429);
}
});
});import { Mutex } from "async-mutex";
const mutex = new Mutex();
async function processOrder(orderId: string) {
const release = await mutex.acquire();
try {
const order = await db.orders.findById(orderId);
if (order.status !== "pending") return;
const charge = await payments.charge(order.total);
order.status = "paid";
order.chargeId = charge.id;
await order.save();
await emails.sendReceipt(order);
} finally {
release();
}
}async function processOrder(orderId: string) {
// invariant: only one caller can move pending → processing
const claimed = await db.orders.updateWhere(
{ id: orderId, status: "pending" },
{ status: "processing" }
);
if (!claimed) return; // another worker got it
const charge = await payments.charge(claimed.total);
await db.orders.update(orderId, {
status: "paid",
chargeId: charge.id,
});
await emails.sendReceipt(claimed);
}<task>Build a hero section for MeetingMind, an AI meeting assistant.</task><visual_direction>- Dark theme with nature-inspired background (forest, mountains, sky) - Premium aesthetic: refined typography, generous whitespace, high contrast - Glass morphism on all interactive elements - Responsive across mobile, tablet, desktop</visual_direction><content>- Headline communicating core value proposition in thin, elegant type - Supporting subheadline with the key benefit - Primary CTA with frosted glass effect</content><done_when>- Semantic HTML, WCAG AA contrast, hover/focus states - Background image optimized for web performance</done_when>
<task>Add rate limiting to the POST /api/submit Express route.</task><context>- Redis client already available at ./lib/redis - authRouter returns 429 errors as { error: string, retryAfter: number }</context><requirements>- Per-IP sliding window: 60s window, 20 requests max - Use Redis INCR + EXPIRE for distributed counting (not in-memory) - Bypass limits for whitelisted IPs and authenticated admins - On exceed, return 429 matching the existing authRouter error shape - Include X-RateLimit-Remaining and X-RateLimit-Reset headers</requirements><done_when>- Typed middleware signature (Request, Response, NextFunction) - Vitest test hits the endpoint 21 times and asserts 429 on the last call</done_when>
<task>Fix the race condition in processOrder.</task><root_cause>The in-process Mutex serializes calls within one Node instance but does nothing across workers. Two workers can both read status === "pending", both charge the card, and both mark the order paid.</root_cause><fix>- Replace the Mutex with an atomic database transition: UPDATE orders SET status = 'processing' WHERE id = ? AND status = 'pending' - Only the first caller gets affectedRows > 0; all others bail immediately - Remove the async-mutex dependency entirely - Add an // invariant: comment above the atomic operation</fix><done_when>- The charge call happens only after the row is exclusively claimed - Failure after charge doesn't leave the order stuck in "processing" - No other shared-state access in the function needs synchronization</done_when>
Make it yours
5 specialized modes
Each mode gives your AI the right context, from API contracts to component accessibility.
Built-in styles
Control tone: professional, technical, creative, casual, or academic.
Developer mode
One toggle reshapes every prompt into structured XML — the format Claude Code, Cursor, and Copilot parse most reliably.
Create your own style
Define tone, formality, and format. brief applies them every time you use AI.
Stop retyping
the same prompts.
Build a library that fits how you actually work. Press ⌘⌥B from any app and brief drops the chosen prompt right where your cursor was.
How brief stacks up
| Product | Works with any AI tool | Real-time inline rewrite | Native to OS | Coding-specific modes | Project context file |
|---|---|---|---|---|---|
| brief | |||||
| PromptPerfectpromptperfect.jina.ai | |||||
| Promptlypromptly.fyi | |||||
| Custom GPTs & InstructionsOpenAI GPT builder | |||||
| IDE rules filesCLAUDE.md, .cursorrules | |||||
| Prompt librariesPromptHub, FlowGPT |
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
- Works with any AI tool
- Real-time inline rewrite
- Native to OS
- Coding-specific modes
- Project context file
Frequently asked questions
Does brief work with Cursor, Copilot, and Claude?
Yes. brief works with any AI tool. It rewrites your prompt before you send it, so it's completely tool-agnostic. Cursor, Copilot, Claude, ChatGPT, or anything else.
Is brief free?
brief is free to start with 10 uses per day. Pro is $9/month for unlimited rewrites, unlimited project folders, and more — see the pricing page for details.
What platforms does brief support?
brief is available as a desktop app for macOS and Windows. It runs as a lightweight menu bar / system tray app and works with any text input.
Is my data stored or sent anywhere?
Your prompts are sent to our API for rewriting and are not stored or logged. brief does not collect, train on, or share your data.
How is this different from just writing a better prompt myself?
You know what good output looks like, but your AI needs specific details to get there. brief's algorithm identifies exactly what's missing and adds it automatically in under a second.
Can I customize how brief rewrites?
Yes. brief has 5 specialized modes, 7 built-in styles, and support for fully custom styles where you define tone, formality, and format. You can also add a context file for project-specific instructions.