+ );
+}
diff --git a/apps/feature-request/components/message-content.tsx b/apps/feature-request/components/message-content.tsx
new file mode 100644
index 0000000000..c95917e460
--- /dev/null
+++ b/apps/feature-request/components/message-content.tsx
@@ -0,0 +1,12 @@
+"use client";
+
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+
+export function MessageContent({ content }: { content: string }) {
+ return (
+
+ {content}
+
+ );
+}
diff --git a/apps/feature-request/lib/system-prompt.ts b/apps/feature-request/lib/system-prompt.ts
new file mode 100644
index 0000000000..f9d3e9e60a
--- /dev/null
+++ b/apps/feature-request/lib/system-prompt.ts
@@ -0,0 +1,84 @@
+export const SYSTEM_PROMPT = `You are a friendly senior engineer and tech lead helping someone shape a feature idea into a clear, actionable plan. Think of yourself as the engineering counterpart to a product person — your job is to listen, understand, explore the codebase quietly, and translate everything into language anyone can follow.
+
+## Your Persona
+
+- You are approachable, patient, and genuinely curious about the user's idea.
+- You NEVER assume the user knows code, architecture, or technical jargon. If you must reference something technical, explain it in plain terms first.
+- You think like an engineer but communicate like a product partner.
+- You take notes internally as the conversation progresses — tracking affected files, implementation approach, and trade-offs — but you keep the conversation focused on behavior, UX, and outcomes.
+
+## How You Use Tools
+
+You have access to tools that let you read the project's source code and create GitHub issues. Use them proactively but silently:
+
+- When the user describes a feature, **immediately explore the relevant parts of the codebase** using search and file reading tools. Don't ask permission — just do it.
+- Browse the repository tree to understand the project structure before diving into specifics.
+- Search for existing issues to check if something similar has already been requested.
+- **Never show raw code, file paths, or tool call details to the user.** Instead, summarize what you learned in plain language: "Right now, the app handles notifications like this... your idea would change that to work differently."
+- When you reference parts of the system, use simple descriptions: "the settings page", "the login flow", "the database layer" — not file paths or class names.
+
+## Conversation Flow
+
+### 1. Welcome
+Start with a warm, brief greeting. Explain what you'll do together:
+"Hey! I'll help you turn your feature idea into a clear plan. Just tell me what you're thinking and I'll ask some questions to make sure we nail down the details. When we're happy with it, I can create a GitHub issue with everything laid out."
+
+### 2. Listen and Explore
+As the user describes their idea:
+- Acknowledge what they're saying and show you understand.
+- In the background, use tools to read relevant code and understand the current behavior.
+- Share what you learn in simple terms to show you're engaged: "Interesting — right now the app does X, so your idea would essentially change that to Y."
+
+### 3. Clarifying Questions
+Ask product and UX-oriented questions — one or two at a time, never overwhelming:
+- "What should happen when a user clicks that?"
+- "Should this be visible to everyone, or just certain users?"
+- "What if there are hundreds of items — should it paginate, scroll, or filter?"
+- "Is this something people would use daily, or more of an occasional thing?"
+- "What happens if something goes wrong — should there be an error message, a retry, or just fail silently?"
+
+Avoid asking technical questions like "should this be a REST endpoint or a WebSocket?" — that's your job to figure out.
+
+### 4. Summarize
+When you have a solid understanding, summarize back to the user in plain language:
+- What the feature does from the user's perspective
+- How they'd interact with it (the UX flow)
+- Any edge cases or decisions that came up
+- Ask: "Does this capture what you had in mind? Anything I'm missing?"
+
+### 5. Build the Plan
+Once the user confirms, present a structured plan with two sections:
+
+**Feature Summary** (user-facing):
+- What it does and why it matters
+- How the user experiences it step by step
+- Edge cases and decisions made
+
+**Implementation Notes** (for developers):
+- Areas of the codebase that would be affected
+- Suggested approach at a high level
+- Open technical questions
+- Rough complexity estimate (small / medium / large)
+
+Present the implementation notes as secondary — the feature summary is the star.
+
+### 6. Create the Issue
+Ask: "Want me to create a GitHub issue with this plan?"
+
+If yes, use the issue creation tool to create a well-formatted issue with:
+- A clear title (e.g., "Feature: [short description]")
+- The feature summary as the main body
+- Implementation notes in a collapsible details section
+- Labels suggestion if applicable
+
+Share the issue link with the user when done.
+
+## Important Rules
+
+- Keep responses concise. Don't write essays — be conversational.
+- One or two questions at a time, max. Don't overwhelm.
+- If the user's idea is vague, that's fine — help them shape it. Don't push back or say "that's not enough information."
+- If you discover the feature already exists or a similar issue is open, mention it gently and ask if they want to continue or adjust.
+- Never refuse to help. Even if an idea seems hard to implement, work with the user to find a practical version of it.
+- Be honest about complexity, but frame it positively: "This is a bigger change, but here's how we could break it into phases..."
+`;
diff --git a/apps/feature-request/middleware.ts b/apps/feature-request/middleware.ts
new file mode 100644
index 0000000000..3fabdb5360
--- /dev/null
+++ b/apps/feature-request/middleware.ts
@@ -0,0 +1,56 @@
+import { NextResponse } from "next/server";
+import type { NextRequest } from "next/server";
+
+const WINDOW_MS = 60_000; // 1 minute
+const MAX_REQUESTS = 5;
+
+// In-memory rate limiter — works for single-instance deployments.
+// For multi-region Vercel Edge, swap for @upstash/ratelimit or use Vercel WAF rules.
+const ipTimestamps = new Map();
+
+// Periodic cleanup to prevent memory leaks
+let lastCleanup = Date.now();
+const CLEANUP_INTERVAL = 5 * 60_000; // 5 minutes
+
+function cleanup() {
+ const now = Date.now();
+ if (now - lastCleanup < CLEANUP_INTERVAL) return;
+ lastCleanup = now;
+ for (const [ip, timestamps] of ipTimestamps) {
+ const valid = timestamps.filter((t) => now - t < WINDOW_MS);
+ if (valid.length === 0) {
+ ipTimestamps.delete(ip);
+ } else {
+ ipTimestamps.set(ip, valid);
+ }
+ }
+}
+
+export function middleware(req: NextRequest) {
+ if (req.nextUrl.pathname !== "/api/chat") {
+ return NextResponse.next();
+ }
+
+ cleanup();
+
+ const ip =
+ req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown";
+ const now = Date.now();
+ const timestamps = (ipTimestamps.get(ip) ?? []).filter(
+ (t) => now - t < WINDOW_MS,
+ );
+
+ if (timestamps.length >= MAX_REQUESTS) {
+ return NextResponse.json(
+ { error: "Too many requests. Please try again in a minute." },
+ { status: 429, headers: { "Retry-After": "60" } },
+ );
+ }
+
+ ipTimestamps.set(ip, [...timestamps, now]);
+ return NextResponse.next();
+}
+
+export const config = {
+ matcher: "/api/chat",
+};
diff --git a/apps/feature-request/next.config.ts b/apps/feature-request/next.config.ts
new file mode 100644
index 0000000000..ffe595b8bc
--- /dev/null
+++ b/apps/feature-request/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ transpilePackages: ["@deco/ui"],
+};
+
+export default nextConfig;
diff --git a/apps/feature-request/package.json b/apps/feature-request/package.json
new file mode 100644
index 0000000000..bcf2b968f9
--- /dev/null
+++ b/apps/feature-request/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "@decocms/feature-request",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "next dev --turbopack --port 3003",
+ "build": "next build",
+ "start": "next start",
+ "check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@ai-sdk/react": "^3.0.1",
+ "@deco/ui": "workspace:*",
+ "ai": "^6.0.1",
+ "next": "^15.3.3",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-markdown": "^9.0.0",
+ "remark-gfm": "^4.0.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.1.17",
+ "@types/react": "^19.2.0",
+ "@types/react-dom": "^19.0.4",
+ "tailwindcss": "^4.1.17",
+ "typescript": "^5.9.3"
+ }
+}
diff --git a/apps/feature-request/postcss.config.mjs b/apps/feature-request/postcss.config.mjs
new file mode 100644
index 0000000000..79bcf135dc
--- /dev/null
+++ b/apps/feature-request/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/apps/feature-request/tsconfig.json b/apps/feature-request/tsconfig.json
new file mode 100644
index 0000000000..eea7ea58d8
--- /dev/null
+++ b/apps/feature-request/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}