feat: migrate off Inngest to new agent architecture#202
feat: migrate off Inngest to new agent architecture#202Jackson57279 wants to merge 19 commits intomasterfrom
Conversation
- Remove Inngest client, functions, and API routes - Add new agent infrastructure (code-generation, error-fixer, framework-selector, validation) - Add streaming support to Convex schema - Update API endpoints for new agent workflow - Replace job orchestration with direct agent execution 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
CodeCapy Review ₍ᐢ•(ܫ)•ᐢ₎Codebase SummaryZapDev is an AI-powered development platform that enables users to create web applications using real-time AI agents. The system features a live split-pane preview, file explorer with syntax highlighting, conversational code generation, and project management. Recently, the application migrated from using Inngest for background job orchestration to a new agent-based architecture integrating code-generation, framework selection, error-fixing, streaming updates via SSE, and validation. The UI components in project and message forms now trigger the new /api/generate endpoint by passing a 'prompt' field instead of 'value'. PR ChangesThis PR removes Inngest client and functions and replaces them with a new agent architecture including multiple agent modules (code-generation, framework-selector, error-fixer, and validation). API endpoints have been updated to support streaming via SSE (/api/generate). The Convex schema has been updated with new streaming endpoints and job orchestration is now performed via direct agent execution. Frontend components (project and message forms) have been updated to call the new API endpoint and send the prompt correctly. Setup Instructions
Generated Test Cases1: SSE Code Generation Streaming Test ❗️❗️❗️Description: Tests the /api/generate endpoint to ensure that when a user submits a code generation request, the server returns an SSE stream with status updates and a final completion event including a summary and generated files. This validates the new agent workflow for code generation and streaming. Prerequisites:
Steps:
Expected Result: The client should continuously receive SSE updates indicating progress (status, stream, and file updates) and eventually a complete event that displays a concise summary of the generated code along with the file data. In case of errors, an error event should be provided. 2: Project Form Submission Data Test ❗️❗️Description: Verifies that the Project Form UI correctly submits the required data to the new /api/generate endpoint using the 'prompt' field, ensuring the removal of legacy 'value' usage. Prerequisites:
Steps:
Expected Result: A network request is sent to /api/generate with the JSON body including the key 'prompt', and the UI indicates the submission is in progress. There should be no reference to the deprecated 'value' field. 3: Error Handling in Code Generation Test ❗️❗️❗️Description: Ensures that when the API encounters an error during code generation, the SSE stream properly sends an 'error' event and the UI displays an appropriate error message. Prerequisites:
Steps:
Expected Result: The SSE stream emits an event with type 'error' that contains a descriptive error message, and the UI (or test client) indicates the failure of the code generation process. 4: Message Form Submission Data Test ❗️❗️Description: Checks that the Message Form UI correctly submits messages to the new /api/generate endpoint using the 'prompt' field, reflecting the changes from the previous Inngest-based implementation. Prerequisites:
Steps:
Expected Result: The submitted message is correctly sent to '/api/generate' with 'prompt' instead of 'value', and the backend begins streaming code generation updates as evidenced by SSE events. 5: UI Visual Update and Progress Indication Test ❗️❗️Description: Verifies the user interface correctly reflects the progress of the AI code generation task by showing live updates, status messages, and final output summary. Prerequisites:
Steps:
Expected Result: The UI should update in real-time with progress messages, display a loading/progress indicator during generation, and eventually show the final generated code summary along with file previews. In error scenarios, clear error notifications should be visible. Raw Changes AnalyzedFile: .cursor/rules/convex_rules.mdc
Changes:
@@ -198,7 +198,7 @@ export const exampleQuery = query({
handler: async (ctx, args) => {
const idToUsername: Record<Id<"users">, string> = {};
for (const userId of args.userIds) {
- const user = await ctx.db.get(userId);
+ const user = await ctx.db.get("users", userId);
if (user) {
idToUsername[user._id] = user.username;
}
@@ -236,8 +236,8 @@ const messages = await ctx.db
## Mutation guidelines
-- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist.
-- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist.
+- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.replace('tasks', taskId, { name: 'Buy milk', completed: false })`
+- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.patch('tasks', taskId, { completed: true })`
## Action guidelines
- Always add `"use node";` to the top of files containing actions that use Node.js built-in modules.
@@ -307,7 +307,7 @@ export const exampleQuery = query({
args: { fileId: v.id("_storage") },
returns: v.null(),
handler: async (ctx, args) => {
- const metadata: FileMetadata | null = await ctx.db.system.get(args.fileId);
+ const metadata: FileMetadata | null = await ctx.db.system.get("_storage", args.fileId);
console.log(metadata);
return null;
},
@@ -434,7 +434,7 @@ Internal Functions:
"description": "This example shows how to build a chat app without authentication.",
"version": "1.0.0",
"dependencies": {
- "convex": "^1.17.4",
+ "convex": "^1.31.2",
"openai": "^4.79.0"
},
"devDependencies": {
@@ -667,6 +667,35 @@ export default defineSchema({
});+#### convex/tsconfig.json
src/App.tsxexport default function App() {
File: AGENTS.md
Changes:
@@ -31,7 +31,6 @@ bun run test # Run Jest tests (if configured)
# Build E2B templates for AI code generation (requires Docker)
cd sandbox-templates/[framework] # nextjs, angular, react, vue, or svelte
e2b template build --name your-template-name --cmd "/compile_page.sh"
-# Update template name in src/inngest/functions.ts after buildingArchitecture Overview@@ -40,47 +39,61 @@ e2b template build --name your-template-name --cmd "/compile_page.sh"
Core ArchitectureAI-Powered Code Generation Flow
Data Flow
Directory StructureKey Components@@ -92,13 +105,14 @@ sandbox-templates/ # E2B sandbox templates for each framework
-Inngest Functions ( Code Standards for AI Agents
E2B TemplatesBefore running AI code generation:
Convex Development
Code Generation Failures
Build or Lint Failures in Sandbox
File: convex/_generated/api.d.ts @@ -36,6 +37,7 @@ declare const fullApi: ApiFromModules<{
File: convex/_generated/dataModel.d.ts
File: convex/schema.ts
File: convex/streaming.ts
+export const getProgress = query({
+export const deleteProgress = mutation({
File: package.json
File: src/agents/agents/code-generation.ts
+interface GenerationResult {
+export async function generateCode(
+function extractSummary(text: string): string {
File: src/agents/agents/error-fixer.ts
File: src/agents/agents/framework-selector.ts
File: src/agents/agents/validation.ts
File: src/agents/client.ts
+export const MODEL_CONFIGS = {
+export type ModelId = keyof typeof MODEL_CONFIGS;
File: src/agents/index.ts File: src/agents/logger.ts
+export function createLogger(taskId: string, extra?: Record<string, unknown>): AgentLogger {
File: src/agents/prompts/index.ts
+const FRAMEWORK_PROMPTS: Record<Framework, string> = {
+export function getFrameworkPrompt(framework: Framework): string {
File: src/agents/retry.ts
+const DEFAULT_OPTIONS: Required = {
+function sleep(ms: number): Promise {
+export async function withRetry(
+export const retryOnRateLimit = (error: Error): boolean => {
+export const retryOnTimeout = (error: Error): boolean => {
+export const retryOnTransient = (error: Error): boolean => {
File: src/agents/sandbox.ts
+export class SandboxManager {
+export const sandboxManager = SandboxManager.getInstance(); File: src/agents/tools.ts
+export type AgentTools = ReturnType; File: src/agents/types.ts
+export interface TaskProgress {
+export interface GenerationRequest {
+export interface ConversationMessage {
+export interface ValidationResult {
+export interface StreamUpdate {
+export interface FileWriteResult {
+export interface TerminalResult {
File: src/app/api/fix-errors/route.ts type FixErrorsRequestBody = {
File: src/app/api/generate/route.ts
+export async function POST(request: NextRequest) {
File: src/app/api/import/figma/direct/route.ts export async function POST(request: Request) {
@@ -40,43 +39,31 @@ export async function POST(request: Request) {
File: src/app/api/import/figma/process/route.ts export async function POST(request: Request) {
@@ -41,12 +36,13 @@ export async function POST(request: Request) {
@@ -74,20 +69,10 @@ export async function POST(request: Request) {
File: src/app/api/inngest/route.ts
|
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughThis PR implements a major architectural migration from an Inngest-based background job system to a new streaming AI agents architecture. It introduces OpenRouter-powered code generation with real-time SSE streaming, replaces Polar billing with Stripe subscriptions, restructures the agent system with modular sandbox management and retry logic, and updates the Convex schema to support task progress tracking and Stripe customer relationships. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client (Browser)
participant API as /api/generate
participant Convex as Convex Backend
participant OpenRouter as OpenRouter AI
participant E2B as E2B Sandbox
participant Stripe as Stripe
Client->>API: POST {projectId, prompt, model}
API->>Convex: Verify user & consume credits
API->>Convex: Create/get streaming message
API->>E2B: Create/connect sandbox
API->>OpenRouter: Stream code generation
OpenRouter-->>API: AI output chunks (streaming)
API->>E2B: Execute tool calls (files, terminal)
E2B-->>API: Tool results
API->>Convex: updateProgress (via streaming)
API->>E2B: runValidation (lint/build)
alt Validation passes
API->>Convex: createFragment + mark COMPLETE
API-->>Client: {type: "complete", files, summary}
else Validation fails
API->>E2B: fixErrors with error-fixer agent
E2B-->>API: Fixed files
API->>E2B: runValidation again
API->>Convex: createFragment + mark COMPLETE
API-->>Client: {type: "complete", files, summary}
end
Client->>Client: Parse SSE stream & update UI
sequenceDiagram
participant Stripe as Stripe Service
participant Webhook as /api/webhooks/stripe
participant Convex as Convex Backend
Stripe->>Webhook: POST event (subscription.created/updated)
Webhook->>Webhook: Verify signature
alt customer.subscription.created/updated
Webhook->>Convex: getCustomer by stripeCustomerId
Webhook->>Convex: createOrUpdateSubscription<br/>(stripeSubscriptionId, status, planName, etc.)
else customer.subscription.deleted
Webhook->>Convex: updateSubscriptionStatus(canceled, canceledAt)
else invoice.payment_succeeded
Webhook->>Convex: updateSubscriptionStatus(active)
else customer.created/updated
Webhook->>Convex: createOrUpdateCustomer(stripeCustomerId, email, name)
end
Webhook-->>Stripe: {received: true}
sequenceDiagram
participant User as User
participant Checkout as /api/stripe/checkout
participant Stripe as Stripe API
participant Convex as Convex Backend
participant Clerk as Clerk Auth
User->>Checkout: Request checkout (priceId)
Checkout->>Clerk: Get user email & name
Checkout->>Convex: Get or create Stripe customer
Convex-->>Checkout: stripeCustomerId
Checkout->>Stripe: Check for active subscription
alt Active subscription exists
Checkout->>Stripe: Create billing portal session
Stripe-->>Checkout: portalSession.url
else No active subscription
Checkout->>Stripe: Create checkout session<br/>(priceId, userId metadata)
Stripe-->>Checkout: checkoutSession.url
end
Checkout-->>User: Return session URL
User->>Stripe: Navigate to Stripe flow
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
|
PR Review: Migration from Inngest to New Agent Architecture - This is a major architectural change. Implementation is solid but has critical security issues. CRITICAL: (1) Unvalidated user input in route.ts line 32-36 needs Zod validation (2) Command injection vulnerability in tools.ts line 94-96 needs path sanitization (3) Stream race condition in route.ts needs writer state checking. IMPORTANT: Missing Sentry context, sandbox cache timer issues, loose v.any types, missing cleanup, duplicate getConvex function, magic numbers, broad retry logic. POSITIVES: Excellent Sentry integration, clean separation, good types, robust retry, clean SSE, smart caching. TESTS: None added - need unit, integration, security tests. RECOMMENDATION: DO NOT MERGE until security issues fixed. Architecture excellent but security first. |
|
SECURITY ISSUE 1 - Unvalidated Input: The API endpoint accepts unvalidated JSON. Add this fix: import z from zod, create GenerateRequestSchema with z.object containing projectId string min 1, prompt string min 1 max 10000, model optional string, sandboxId string min 1, messageId optional string. Then parseAsync the request body with this schema before use. |
|
SECURITY ISSUE 2 - Command Injection: In tools.ts listFiles function, the path variable is directly interpolated into a shell command on line 94-96. An attacker can inject commands through the path parameter. Fix by adding: const sanitizedPath equals path.replace with regex to remove semicolons, ampersands, pipes, angle brackets, dollar signs, and parentheses. Or better yet, use sandbox.files.list API if available instead of shell commands. |
|
SECURITY ISSUE 3 - Stream Race Condition: In route.ts the sendUpdate function writes to the stream without checking if the client disconnected. If client drops connection, writer.write throws but code continues. Fix: wrap sendUpdate body in try-catch, check if writer.desiredSize is null (indicates closed), return early if closed, catch any write errors and log them without throwing. |
|
CODE QUALITY IMPROVEMENTS: (1) Duplicate getConvex function exists in both code-generation.ts and generate route.ts - extract to src/lib/convex-client.ts. (2) Magic numbers in validation.ts for timeouts 30000 and 120000 should be named constants LINT_TIMEOUT_MS and BUILD_TIMEOUT_MS. (3) In sandbox.ts line 99 replace console.warn with Sentry logger for consistency. (4) Add JSDoc comments to public agent functions explaining params, returns, and error conditions. |
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/api/import/figma/process/route.ts (1)
28-45: Improve TypeScript typing for the OAuth connection rather than relying on unsafe type assertions.The
accessTokenfield is properly defined in the schema (convex/schema.tsline 130), so the manual type assertion on line 39 is a workaround for incomplete TypeScript type generation from the generated Convex API client. Instead of casting, consider:
- Verify that
api.oauth.getConnectionis properly typed in your generated Convex API types to include alloauthConnectionsfields- If type generation is the issue, update your Convex setup or add explicit type helpers
- The null check at lines 32-36 correctly handles missing connections; no additional runtime validation is needed if the types are correct
The removal of
(api as any)on line 28 is a good improvement over the other route files.
🧹 Nitpick comments (20)
src/app/api/transfer-sandbox/route.ts (1)
19-25: Update error message for consistency.The error message references "Failed to resume sandbox", but since the endpoint currently doesn't perform any operation, this message could be confusing during debugging.
🔎 Suggested update
- console.error("[ERROR] Failed to resume sandbox:", error); + console.error("[ERROR] Sandbox transfer endpoint error:", error); return NextResponse.json( - { error: "Failed to resume sandbox" }, + { error: "Sandbox transfer endpoint error" }, { status: 500 } );src/app/api/import/figma/process/route.ts (3)
54-54: Consider more specific typing for the Figma API response.The type uses
pages?: unknown[]which provides minimal type safety. If the Figma API response structure is known, define a more specific interface.📝 Suggested improvement
Define a proper interface for the Figma file response:
interface FigmaFileResponse { name: string; lastModified: string; version: string; pages?: Array<{ id: string; name: string; type: string; }>; } const fileData = await fileResponse.json() as FigmaFileResponse;
56-70: Improved type safety, but validateprojectIdbefore casting.The removal of the
anycast is excellent. However, castingprojectId as Id<"projects">assumes the input is a valid project ID without runtime verification.Consider validating the project exists and the user has access:
🔎 Recommended validation
// Verify project exists and user has access before creating import const project = await fetchQuery(api.projects.get, { id: projectId as Id<"projects"> }); if (!project) { return NextResponse.json( { error: "Project not found" }, { status: 404 } ); } const importRecord = await fetchMutation(api.imports.createImport, { projectId: projectId as Id<"projects">, source: "FIGMA", // ... rest of fields });
77-83: Error handling is adequate but could be more specific.The generic catch-all error handler is functional and correctly avoids exposing internal error details to clients. However, different error types (auth failures, network errors, validation errors) could benefit from more specific handling.
📝 Optional improvement for error categorization
} catch (error) { console.error("Error processing Figma import:", error); // Provide more specific errors based on error type if (error instanceof TypeError && error.message.includes("fetch")) { return NextResponse.json( { error: "Failed to connect to Figma API" }, { status: 502 } ); } return NextResponse.json( { error: "Failed to process Figma import" }, { status: 500 } ); }.cursor/rules/convex_rules.mdc (1)
670-697: Verify the necessity of this duplicate convex/tsconfig.json block.A convex/tsconfig.json example block appears to be added twice in the documentation. Please verify if this is intentional or if one occurrence should be removed to avoid confusion.
src/agents/agents/error-fixer.ts (1)
61-63: Recursive retry is acceptable but consider logging attempt progression.The recursive approach works for a bounded depth (max 2), but each recursion creates new logger instances. Consider passing the original logger or tracking cumulative state for better observability across retry attempts.
src/agents/prompts/index.ts (1)
29-31: Redundant fallback when using typed parameter.Since
frameworkis typed asFramework, the lookupFRAMEWORK_PROMPTS[framework]will always succeed. The|| NEXTJS_PROMPTfallback is defensive but unreachable with strict typing.This is fine to keep for safety, but consider adding a comment explaining it guards against runtime type mismatches from external inputs.
src/agents/agents/framework-selector.ts (2)
32-32: Consider stricter framework matching.Using
result.includes(f)could potentially match substrings incorrectly (e.g., matching "react" within "react-native" or other compound terms). Consider using word boundary matching or exact matching.🔎 Proposed fix
- const framework = VALID_FRAMEWORKS.find((f) => result.includes(f)) || 'nextjs'; + const framework = VALID_FRAMEWORKS.find((f) => + result === f || result.includes(` ${f}`) || result.startsWith(`${f} `) || result.endsWith(` ${f}`) + ) || 'nextjs';Or simpler - since the AI should return just the framework name:
- const framework = VALID_FRAMEWORKS.find((f) => result.includes(f)) || 'nextjs'; + const framework = VALID_FRAMEWORKS.includes(result as Framework) + ? (result as Framework) + : VALID_FRAMEWORKS.find((f) => result.includes(f)) || 'nextjs';
5-5: Use type-only import forFramework.Since
Frameworkis only used as a type annotation, use a type-only import to ensure it's erased at compile time.-import { Framework } from '../types'; +import type { Framework } from '../types';src/agents/tools.ts (2)
66-78: Redundant nullish coalescing for timeoutMs.The default parameter
timeoutMs = 60000already handles undefined values, makingtimeoutMs ?? 60000redundant on line 78.- const result = await sandbox.commands.run(command, { timeoutMs: timeoutMs ?? 60000 }); + const result = await sandbox.commands.run(command, { timeoutMs });
50-52: Avoid exposing raw error details in file read responses.Including the raw error object in the response could leak internal details. Consider using a sanitized message.
} catch (error) { - files[path] = `[Error reading file: ${error}]`; + files[path] = `[Error reading file: File not found or inaccessible]`; }src/agents/agents/code-generation.ts (2)
13-21: Duplicate getConvex helper - consider extracting to shared utility.This
getConvex()pattern is duplicated insrc/app/api/generate/route.ts. Consider extracting to a shared module likesrc/lib/convex.tsto avoid duplication.// src/lib/convex-client.ts import { ConvexHttpClient } from 'convex/browser'; let _convex: ConvexHttpClient | null = null; export function getConvex(): ConvexHttpClient { if (!_convex) { const url = process.env.NEXT_PUBLIC_CONVEX_URL; if (!url) throw new Error('NEXT_PUBLIC_CONVEX_URL is not set'); _convex = new ConvexHttpClient(url); } return _convex; }
86-93: Tool call handling with type assertion is fragile.The
'input' in callcheck and subsequent castas { files: Array<...> }assumes a specific structure. Consider adding runtime validation.🔎 Proposed fix
for (const call of toolCalls) { - if (call.toolName === 'createOrUpdateFiles' && 'input' in call) { - const input = call.input as { files: Array<{ path: string; content: string }> }; + if (call.toolName === 'createOrUpdateFiles' && 'args' in call && call.args) { + const input = call.args as { files?: Array<{ path: string; content: string }> }; + if (!input.files) continue; for (const file of input.files) { files[file.path] = file.content; } } }src/agents/sandbox.ts (3)
32-35: Add validation for missing API key.The non-null assertion on
process.env.E2B_API_KEY!will cause a runtime error with an unclear message if the environment variable is not set. Consider validating early or providing a clear error.🔎 Proposed validation
async connect(sandboxId: string): Promise<Sandbox> { const cached = SANDBOX_CACHE.get(sandboxId); if (cached) { return cached; } + const apiKey = process.env.E2B_API_KEY; + if (!apiKey) { + throw new Error('E2B_API_KEY environment variable is not configured'); + } + try { const sandbox = await Sandbox.connect(sandboxId, { - apiKey: process.env.E2B_API_KEY!, + apiKey, });
91-105: Silent file read failures may cause unexpected behavior.When a file cannot be read, the error is logged but the file is silently omitted from the result. Callers expecting specific files may not realize they're missing. Consider returning an explicit error indicator or throwing when critical files fail.
🔎 Alternative approach with error tracking
- async readFiles(sandbox: Sandbox, paths: string[]): Promise<Record<string, string>> { - const files: Record<string, string> = {}; + async readFiles( + sandbox: Sandbox, + paths: string[] + ): Promise<{ files: Record<string, string>; errors: string[] }> { + const files: Record<string, string> = {}; + const errors: string[] = []; await Promise.all( paths.map(async (path) => { try { files[path] = await sandbox.files.read(path); } catch (error) { - console.warn(`Failed to read file ${path}:`, error); + errors.push(path); + console.warn(`Failed to read file ${path}:`, error); } }) ); - return files; + return { files, errors }; }
120-125: The?? 0fallback is defensive but unnecessary. The E2B SDK raises exceptions (CommandExitError, TimeoutError) for failures and timeouts—these don't return null exitCode values. Once theawaitcompletes,exitCodeshould be set to a number per the SDK specification. The fallback adds no real safety since failures are already handled via exception propagation.src/agents/retry.ts (2)
61-64: Unreachable code after retry loop.Line 63 is unreachable because the
forloop will either return on success (line 33) or throw on final failure/non-retryable error (line 55). Thethrow lastErrorstatement after the loop can never execute.🔎 Remove unreachable code
await sleep(delay); delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay); } } - - throw lastError; }
66-85: Error predicates are case-sensitive.The string matching (e.g.,
'rate limit','timeout') won't match variations like'Rate Limit'or'TIMEOUT'. Consider case-insensitive matching for robustness.🔎 Case-insensitive matching
export const retryOnRateLimit = (error: Error): boolean => { + const msg = error.message.toLowerCase(); return ( - error.message.includes('rate limit') || - error.message.includes('429') || - error.message.includes('too many requests') + msg.includes('rate limit') || + msg.includes('429') || + msg.includes('too many requests') ); }; export const retryOnTimeout = (error: Error): boolean => { - return error.message.includes('timeout') || error.message.includes('ETIMEDOUT'); + const msg = error.message.toLowerCase(); + return msg.includes('timeout') || msg.includes('etimedout'); };convex/streaming.ts (1)
11-11: Avoidv.any()for the files field.Using
v.any()bypasses type safety. Per coding guidelines, prefer explicit validators. If files is a string-to-string record, usev.record(v.string(), v.string()).🔎 Use explicit record type
- files: v.optional(v.any()), + files: v.optional(v.record(v.string(), v.string())),As per coding guidelines, avoid using
anytype and use explicit validators.AGENTS.md (1)
68-97: Add language specifier to fenced code block.The directory structure code block is missing a language specifier, which triggers the MD040 lint warning. Adding a language improves rendering consistency.
🔎 Add language specifier
-``` +```text src/ agents/ # AI agent architecture
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
bun.lockis excluded by!**/*.lockconvex/_generated/api.d.tsis excluded by!**/_generated/**convex/_generated/dataModel.d.tsis excluded by!**/_generated/**
📒 Files selected for processing (35)
.cursor/rules/convex_rules.mdcAGENTS.mdconvex/schema.tsconvex/streaming.tspackage.jsonsrc/agents/agents/code-generation.tssrc/agents/agents/error-fixer.tssrc/agents/agents/framework-selector.tssrc/agents/agents/validation.tssrc/agents/client.tssrc/agents/index.tssrc/agents/logger.tssrc/agents/prompts/index.tssrc/agents/retry.tssrc/agents/sandbox.tssrc/agents/tools.tssrc/agents/types.tssrc/app/api/fix-errors/route.tssrc/app/api/generate/route.tssrc/app/api/import/figma/direct/route.tssrc/app/api/import/figma/process/route.tssrc/app/api/inngest/route.tssrc/app/api/inngest/trigger/route.tssrc/app/api/test-inngest/route.tssrc/app/api/transfer-sandbox/route.tssrc/inngest/client.tssrc/inngest/functions.tssrc/inngest/functions/auto-pause.tssrc/inngest/functions/process-figma-direct.tssrc/inngest/functions/process-figma-import.tssrc/inngest/functions/process-github-import.tssrc/inngest/types.tssrc/inngest/utils.tssrc/modules/home/ui/components/project-form.tsxsrc/modules/projects/ui/components/message-form.tsx
💤 Files with no reviewable changes (11)
- src/inngest/client.ts
- src/inngest/functions/auto-pause.ts
- src/app/api/inngest/trigger/route.ts
- src/inngest/functions/process-github-import.ts
- src/app/api/inngest/route.ts
- src/app/api/test-inngest/route.ts
- src/inngest/functions/process-figma-direct.ts
- src/inngest/utils.ts
- src/inngest/functions.ts
- src/inngest/functions/process-figma-import.ts
- src/inngest/types.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/app/api/generate/route.tssrc/agents/logger.tssrc/agents/agents/framework-selector.tssrc/agents/tools.tssrc/agents/agents/validation.tssrc/app/api/transfer-sandbox/route.tssrc/app/api/fix-errors/route.tssrc/agents/agents/code-generation.tssrc/agents/client.tssrc/agents/sandbox.tssrc/agents/agents/error-fixer.tssrc/agents/index.tssrc/agents/prompts/index.tssrc/agents/retry.tssrc/modules/projects/ui/components/message-form.tsxsrc/app/api/import/figma/process/route.tssrc/agents/types.tssrc/modules/home/ui/components/project-form.tsxsrc/app/api/import/figma/direct/route.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification
Files:
src/app/api/generate/route.tssrc/app/api/transfer-sandbox/route.tssrc/app/api/fix-errors/route.tssrc/app/api/import/figma/process/route.tssrc/app/api/import/figma/direct/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/app/api/generate/route.tssrc/agents/logger.tssrc/agents/agents/framework-selector.tsconvex/schema.tssrc/agents/tools.tssrc/agents/agents/validation.tssrc/app/api/transfer-sandbox/route.tssrc/app/api/fix-errors/route.tssrc/agents/agents/code-generation.tssrc/agents/client.tssrc/agents/sandbox.tssrc/agents/agents/error-fixer.tssrc/agents/index.tssrc/agents/prompts/index.tssrc/agents/retry.tssrc/modules/projects/ui/components/message-form.tsxconvex/streaming.tssrc/app/api/import/figma/process/route.tssrc/agents/types.tssrc/modules/home/ui/components/project-form.tsxsrc/app/api/import/figma/direct/route.ts
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Always use
bunfor package management (bun install, bun add, bun remove). Never use npm or yarn.
Files:
package.json
convex/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Convex database (
src/api/convex/*) for all new data operations; never use deprecated PostgreSQL schema (prisma/)
Files:
convex/schema.tsconvex/streaming.ts
convex/schema.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in
usagetable with rolling 24-hour expiration
Files:
convex/schema.ts
convex/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
convex/**/*.{ts,tsx}: ALWAYS use the new function syntax for Convex functions with explicitargs,returns, andhandlerproperties
Always use validators from thevmodule for array types, such asv.array(v.union(v.string(), v.number()))
Always use thev.null()validator when returning a null value from a Convex function
v.bigint()is deprecated for representing signed 64-bit integers; usev.int64()instead
Usev.record()for defining record types;v.map()andv.set()are not supported in Convex
UseinternalQuery,internalMutation, andinternalActionto register private functions that are not part of the public API
Usequery,mutation, andactionto register public functions that are exposed to the public Internet
ALWAYS include argument and return validators for all Convex functions, includingquery,internalQuery,mutation,internalMutation,action, andinternalAction
Usectx.runQueryto call a query from a query, mutation, or action; specify type annotations on return values when calling functions in the same file
Usectx.runMutationto call a mutation from a mutation or action
Usectx.runActionto call an action from an action; otherwise pull out shared code into a helper async function
Use theapiobject fromconvex/_generated/api.tsto call public functions registered withquery,mutation, oraction
Use theinternalobject fromconvex/_generated/api.tsto call internal functions registered withinternalQuery,internalMutation, orinternalAction
Index fields must be queried in the same order they are defined in the schema index
Use the helper TypeScript typeId<'tableName'>imported from'convex/_generated/dataModel'to get the type of document IDs for a table
When defining aRecordtype, provide correct TypeScript types for both key and value; for example,Record<Id<'users'>, string>
Be strict with types, particularly around IDs of documents; useId<'tableName'>rather than `stri...
Files:
convex/schema.tsconvex/streaming.ts
convex/schema.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)
convex/schema.{ts,tsx}: Always use schema validators with discriminated union types usingv.union()withv.literal()for the kind field
Always define your schema inconvex/schema.tsand import schema definition functions fromconvex/server
Always include all index fields in the index name; for example, an index on ['field1', 'field2'] should be named 'by_field1_and_field2'
Files:
convex/schema.ts
**/*.md
📄 CodeRabbit inference engine (.cursor/rules/rules.mdc)
Minimize the creation of .md files; if necessary, place them in the @explanations folder
Files:
AGENTS.md
*.md
📄 CodeRabbit inference engine (AGENTS.md)
Documentation files should be placed in
explanations/folder, not in the root directory
Files:
AGENTS.md
🧠 Learnings (42)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/app/api/generate/route.tssrc/agents/agents/framework-selector.tssrc/agents/tools.tssrc/agents/agents/code-generation.tssrc/agents/index.tssrc/agents/prompts/index.tssrc/modules/projects/ui/components/message-form.tsxsrc/agents/types.tssrc/modules/home/ui/components/project-form.tsxAGENTS.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/**/*.{ts,tsx} : Use modern framework patterns: Next.js App Router and React hooks
Applied to files:
src/app/api/generate/route.tssrc/agents/prompts/index.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/instrumentation.ts : Leverage Sentry integration (`src/instrumentation.ts`) for production error tracking and OpenTelemetry for distributed tracing
Applied to files:
src/agents/logger.tssrc/agents/retry.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/framework-selector.ts : Support framework auto-detection priority: Explicit user mention → default Next.js → Enterprise indicators (Angular) → Material Design preference (Angular/Vue) → Performance critical (Svelte)
Applied to files:
src/agents/agents/framework-selector.tssrc/agents/prompts/index.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/framework-selector.ts : Use Next.js 15 as default framework for web apps unless user explicitly specifies otherwise; check `src/prompts/framework-selector.ts` for detection logic
Applied to files:
src/agents/agents/framework-selector.tssrc/agents/prompts/index.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/prompts/*.ts : Framework-specific AI prompts must be maintained in `src/prompts/` with separate files per framework (nextjs.ts, angular.ts, etc.)
Applied to files:
src/agents/agents/framework-selector.tssrc/agents/agents/code-generation.tssrc/agents/index.tssrc/agents/prompts/index.tssrc/modules/projects/ui/components/message-form.tsxsrc/agents/types.tssrc/modules/home/ui/components/project-form.tsxAGENTS.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/**/*.ts : Use Convex database (`src/api/convex/*`) for all new data operations; never use deprecated PostgreSQL schema (`prisma/`)
Applied to files:
convex/schema.tsconvex/streaming.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/schema.{ts,tsx} : Always define your schema in `convex/schema.ts` and import schema definition functions from `convex/server`
Applied to files:
convex/schema.tssrc/app/api/fix-errors/route.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use Convex real-time database subscriptions to enable UI updates when data changes in `projects`, `messages`, `fragments`, `usage`, `oauthConnections`, and `imports` tables
Applied to files:
convex/schema.tsconvex/streaming.ts.cursor/rules/convex_rules.mdcAGENTS.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Index fields must be queried in the same order they are defined in the schema index
Applied to files:
convex/schema.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the helper TypeScript type `Id<'tableName'>` imported from `'convex/_generated/dataModel'` to get the type of document IDs for a table
Applied to files:
convex/schema.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Query the `_storage` system table using `ctx.db.system.get()` instead of the deprecated `ctx.storage.getMetadata` for file metadata
Applied to files:
convex/schema.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/schema.ts : Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in `usage` table with rolling 24-hour expiration
Applied to files:
convex/schema.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/agents/tools.tssrc/agents/agents/validation.tssrc/agents/agents/code-generation.tssrc/agents/sandbox.tsAGENTS.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/tools.tssrc/agents/agents/code-generation.tssrc/agents/sandbox.tssrc/agents/types.tsAGENTS.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/agents/agents/validation.tssrc/agents/sandbox.tssrc/agents/agents/error-fixer.tsAGENTS.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/validation.tssrc/app/api/fix-errors/route.tssrc/agents/agents/code-generation.tssrc/agents/agents/error-fixer.tssrc/agents/retry.tsAGENTS.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Never start dev servers in E2B sandboxes - only run build and lint validation
Applied to files:
src/agents/agents/validation.tsAGENTS.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `api` object from `convex/_generated/api.ts` to call public functions registered with `query`, `mutation`, or `action`
Applied to files:
src/app/api/fix-errors/route.tssrc/modules/projects/ui/components/message-form.tsxconvex/streaming.tssrc/app/api/import/figma/process/route.ts.cursor/rules/convex_rules.mdcsrc/modules/home/ui/components/project-form.tsxsrc/app/api/import/figma/direct/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `internal` object from `convex/_generated/api.ts` to call internal functions registered with `internalQuery`, `internalMutation`, or `internalAction`
Applied to files:
src/app/api/fix-errors/route.tsconvex/streaming.tssrc/app/api/import/figma/process/route.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/auth.config.ts : Keep OAuth tokens encrypted in Convex; never expose API keys in client-side code (use NEXT_PUBLIC_ prefix only for public values)
Applied to files:
src/app/api/fix-errors/route.tsAGENTS.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
Applied to files:
src/app/api/fix-errors/route.tssrc/modules/projects/ui/components/message-form.tsxAGENTS.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/functions.ts : Update E2B template name in `src/inngest/functions.ts` (line ~22) after building new templates
Applied to files:
src/modules/projects/ui/components/message-form.tsxsrc/modules/home/ui/components/project-form.tsxAGENTS.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `internalQuery`, `internalMutation`, and `internalAction` to register private functions that are not part of the public API
Applied to files:
convex/streaming.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `query`, `mutation`, and `action` to register public functions that are exposed to the public Internet
Applied to files:
convex/streaming.tssrc/app/api/import/figma/process/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : ALWAYS include argument and return validators for all Convex functions, including `query`, `internalQuery`, `mutation`, `internalMutation`, `action`, and `internalAction`
Applied to files:
convex/streaming.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : ALWAYS use the new function syntax for Convex functions with explicit `args`, `returns`, and `handler` properties
Applied to files:
convex/streaming.ts.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `ctx.db.replace` to fully replace an existing document; this method throws an error if the document does not exist
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `ctx.db.patch` to shallow merge updates into an existing document; this method throws an error if the document does not exist
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Never use `ctx.db` inside of an action; actions do not have access to the database
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Be strict with types, particularly around IDs of documents; use `Id<'tableName'>` rather than `string` for function parameters
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `ctx.runMutation` to call a mutation from a mutation or action
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `ctx.storage.getUrl()` to get signed URLs for files; it returns `null` if the file doesn't exist
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always define arrays as `const array: Array<T> = [...]` with explicit type annotations
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always add `types/node` to your `package.json` when using any Node.js built-in modules
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always define records as `const record: Record<KeyType, ValueType> = {...}` with explicit type annotations
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `v.record()` for defining record types; `v.map()` and `v.set()` are not supported in Convex
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always add `'use node';` to the top of files containing actions that use Node.js built-in modules
Applied to files:
.cursor/rules/convex_rules.mdc
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Organize files thoughtfully in the `convex/` directory using file-based routing for public query, mutation, and action functions
Applied to files:
AGENTS.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Applied to files:
AGENTS.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/compile_page.sh : E2B sandbox execution has 60-minute maximum timeout per sandbox instance
Applied to files:
AGENTS.md
🧬 Code graph analysis (11)
src/app/api/generate/route.ts (6)
tests/mocks/convex-browser.ts (1)
ConvexHttpClient(1-11)src/agents/types.ts (1)
StreamUpdate(43-50)convex/_generated/dataModel.d.ts (1)
Id(48-49)src/agents/agents/code-generation.ts (1)
generateCode(28-115)src/agents/agents/validation.ts (1)
runValidation(5-35)src/agents/agents/error-fixer.ts (1)
fixErrors(22-69)
src/agents/logger.ts (3)
src/agents/index.ts (2)
AgentLogger(8-8)createLogger(8-8)test-e2b-sandbox.js (1)
result(67-67)src/lib/performance.ts (1)
name(66-70)
src/agents/agents/framework-selector.ts (5)
src/agents/types.ts (1)
Framework(3-3)src/agents/logger.ts (1)
createLogger(67-69)src/agents/retry.ts (2)
withRetry(23-64)retryOnTransient(78-85)src/agents/client.ts (1)
getModel(37-40)src/agents/prompts/index.ts (1)
FRAMEWORK_SELECTOR_PROMPT(16-16)
src/agents/tools.ts (2)
src/agents/index.ts (2)
createTools(9-9)AgentTools(10-10)tests/mocks/e2b-code-interpreter.ts (1)
Sandbox(1-37)
src/agents/agents/validation.ts (5)
src/agents/index.ts (3)
runValidation(16-16)createLogger(8-8)sandboxManager(6-6)src/agents/types.ts (1)
ValidationResult(37-41)src/agents/logger.ts (1)
createLogger(67-69)test-e2b-sandbox.js (1)
sandbox(31-31)src/agents/sandbox.ts (1)
sandboxManager(129-129)
src/agents/agents/code-generation.ts (7)
src/agents/types.ts (3)
GenerationRequest(24-30)StreamUpdate(43-50)Framework(3-3)src/agents/logger.ts (1)
createLogger(67-69)src/agents/sandbox.ts (1)
sandboxManager(129-129)src/agents/tools.ts (1)
createTools(6-101)src/agents/retry.ts (2)
withRetry(23-64)retryOnTransient(78-85)src/agents/client.ts (2)
getModel(37-40)ModelId(35-35)src/agents/prompts/index.ts (1)
getFrameworkPrompt(29-31)
src/agents/agents/error-fixer.ts (6)
src/agents/types.ts (2)
StreamUpdate(43-50)ValidationResult(37-41)src/agents/logger.ts (1)
createLogger(67-69)src/agents/sandbox.ts (1)
sandboxManager(129-129)src/agents/tools.ts (1)
createTools(6-101)src/agents/client.ts (1)
getModel(37-40)src/agents/agents/validation.ts (1)
runValidation(5-35)
src/agents/prompts/index.ts (2)
src/agents/types.ts (1)
Framework(3-3)src/agents/index.ts (1)
getFrameworkPrompt(12-12)
src/agents/retry.ts (2)
src/agents/logger.ts (1)
error(41-49)src/agents/index.ts (4)
withRetry(7-7)retryOnRateLimit(7-7)retryOnTimeout(7-7)retryOnTransient(7-7)
src/app/api/import/figma/process/route.ts (2)
types/convex-extended-api.d.ts (1)
api(10-20)convex/_generated/dataModel.d.ts (1)
Id(48-49)
src/app/api/import/figma/direct/route.ts (1)
convex/_generated/dataModel.d.ts (1)
Id(48-49)
🪛 GitHub Check: CodeQL
src/agents/logger.ts
[failure] 19-19: Use of externally-controlled format string
Format string depends on a user-provided value.
[failure] 31-31: Use of externally-controlled format string
Format string depends on a user-provided value.
🪛 markdownlint-cli2 (0.18.1)
AGENTS.md
48-48: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
61-61: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
68-68: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
165-165: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: claude-review
- GitHub Check: Codacy Security Scan
🔇 Additional comments (26)
src/app/api/transfer-sandbox/route.ts (1)
8-13: LGTM!Input validation is appropriate and follows best practices.
src/app/api/import/figma/process/route.ts (1)
5-5: LGTM! Good addition for type safety.The
Idtype import enables proper type checking for Convex document IDs, improving type safety at line 57.src/app/api/import/figma/direct/route.ts (2)
49-61: LGTM - Proper type safety for Convex mutation call.The use of
Id<"projects">type assertion and the updatedapi.imports.createImportcall follows Convex best practices for strict typing.As per coding guidelines, using
Id<'tableName'>rather thanstringfor document IDs is the correct approach.
64-67: Figma import processing is not yet implemented.The success response indicates that Figma import processing is not yet implemented in the new architecture. This creates a potentially confusing user experience where the API accepts the request but doesn't process it.
Please clarify:
- Is this a temporary placeholder during the migration?
- Should this endpoint return a 501 (Not Implemented) status instead of 200 with a success message?
- Is there a tracking issue for implementing Figma import in the new agent architecture?
.cursor/rules/convex_rules.mdc (3)
239-240: LGTM - Clearer mutation examples with explicit table names.The updated examples for
ctx.db.replaceandctx.db.patchnow include explicit table names as the first parameter, making the API usage clearer and more consistent.
310-310: LGTM - Corrected storage metadata retrieval pattern.The updated example correctly uses
ctx.db.system.get("_storage", args.fileId)instead of the deprecatedctx.storage.getMetadataapproach.Based on learnings, this aligns with the guideline to query the
_storagesystem table usingctx.db.system.get().
201-201: Thectx.db.get("users", userId)pattern is correct for Convex 1.31.2. This API was updated in v1.31.0 to require the table name as the first argument for safer validation and v.id() support. No changes needed.src/modules/projects/ui/components/message-form.tsx (2)
13-13: LGTM - Updated import path reflects new agent architecture.The ModelId type import now correctly points to
@/agents/clientinstead of the removed@/inngest/functionsmodule, aligning with the PR's migration to the new agent architecture.
85-93: LGTM - API endpoint and payload updated for new architecture.The changes correctly update the trigger mechanism:
- Endpoint changed from
/api/inngest/triggerto/api/generate- Payload field renamed from
valuetopromptfor better semantic clarity- Model selection properly included in the request
These changes align with the migration from Inngest-based orchestration to the new agent-based workflow.
Based on learnings, the old flow was: User input → Convex messages → Inngest code-agent/run → Code generation. The new flow uses the /api/generate endpoint directly.
src/modules/home/ui/components/project-form.tsx (2)
15-15: LGTM - Updated import path reflects new agent architecture.Consistent with the changes in message-form.tsx, the ModelId type import now correctly points to
@/agents/client.
79-87: LGTM - Consistent API migration across components.The endpoint and payload changes mirror those in message-form.tsx, ensuring consistent behavior across the application. The migration from
/api/inngest/triggerto/api/generatewith thepromptfield is correct.src/app/api/fix-errors/route.ts (1)
55-58: Error fix feature not yet implemented in new architecture.The endpoint now returns a placeholder message instead of triggering error fixes. This creates a confusing user experience where:
- The API accepts the request (200 status)
- Performs authorization and validation checks
- But doesn't actually fix any errors
Consider one of these approaches:
- Return HTTP 501 (Not Implemented) to clearly indicate the feature isn't ready
- Remove the endpoint temporarily until the error-fix agent is implemented
- Add a tracking issue reference in the response message
Based on learnings, the old architecture used Inngest for auto-fix retry logic (max 2 attempts on lint/build errors). Is there an equivalent implementation planned for the new agent architecture?
src/agents/agents/validation.ts (1)
5-35: LGTM - Clean validation implementation with proper error handling.The validation logic correctly:
- Creates a scoped logger for tracking
- Connects to the sandbox
- Runs lint and build sequentially with appropriate timeouts
- Returns structured results indicating the failure type
- Logs progress at each stage
The implementation aligns with learnings about validation workflow: running lint before build, with clear error categorization.
Based on learnings, always run lint and build for validation in sandboxes after code generation, with auto-fix logic detecting SyntaxError, TypeError, and Build failed patterns.
package.json (1)
15-15: Both package versions are verified and stable on npm.The versions specified in the PR are confirmed:
@ai-sdk/openai@3.0.1andai@6.0.3both exist and are the current latest stable releases on npm. No stability concerns.src/agents/agents/error-fixer.ts (1)
22-36: LGTM on retry logic and max attempts.The implementation correctly limits auto-fix attempts to 2 (matching the documented retry policy from learnings). The early return with a descriptive error message when max attempts are reached is appropriate.
src/agents/prompts/index.ts (1)
1-27: LGTM on prompt organization.The module correctly centralizes framework-specific prompts with proper re-exports and a typed mapping. This aligns with the coding guidelines requiring framework-specific prompts in dedicated files. Based on learnings, Next.js is correctly used as the default framework.
src/app/api/generate/route.ts (1)
51-125: IIFE streaming pattern is appropriate for SSE.The async IIFE pattern correctly handles the streaming lifecycle, with proper error handling via Sentry and cleanup in the finally block. The stream headers are correctly configured for SSE with buffering disabled.
src/agents/agents/framework-selector.ts (1)
10-36: LGTM on retry and default framework logic.The implementation correctly defaults to 'nextjs' when detection fails, aligning with the documented priority. The retry mechanism with transient error handling is appropriate for external API calls. Based on learnings, Next.js as default is the correct behavior.
src/agents/tools.ts (1)
6-32: LGTM on createOrUpdateFiles tool.The file writing tool correctly handles batch operations, emits Sentry breadcrumbs for observability, and invokes the optional callback for each written file. The Zod schema provides proper validation.
src/agents/client.ts (2)
37-39: Fallback in getModel is defensive but type-safe.The
|| MODEL_CONFIGS['auto']fallback is unreachable whenmodelIdis properly typed asModelId. This is acceptable defensive coding for cases where the input might come from external sources without strict typing.
12-33: LGTM on model configuration structure.The MODEL_CONFIGS object with
as constprovides proper type inference for ModelId. The configuration structure is clean and extensible for adding new models.src/agents/logger.ts (1)
41-64: LGTM on error handling and span tracking.The error method correctly captures exceptions to Sentry with appropriate context. The startSpan method properly wraps async operations for distributed tracing. The complete method records task duration as a Sentry measurement.
src/agents/agents/code-generation.ts (1)
117-125: LGTM on extractSummary helper.The summary extraction logic is clean with a reasonable fallback chain: tagged summary → first paragraph (truncated) → default message.
src/agents/index.ts (1)
1-17: LGTM!Clean barrel file with well-organized exports. The separation of value exports (
export { ... }) from type exports (export type { ... }) follows TypeScript best practices.AGENTS.md (1)
42-64: Documentation accurately reflects the new architecture.The updates to the architecture overview correctly document the migration from Inngest to the new SSE-based agent system, including the updated data flow and component responsibilities. Based on learnings, this supersedes the previous Inngest-based workflow documented in AGENTS.md and CLAUDE.md.
src/agents/types.ts (1)
1-61: LGTM!Comprehensive and well-structured type definitions. The types align with the new agent architecture:
Frameworktype matches sandbox templatesTaskProgressmirrors the Convex schema structureStreamUpdateproperly defines SSE event shapes- No use of
anytype, following coding guidelines
|
ADDITIONAL ISSUES: (1) Sandbox cache in sandbox.ts creates multiple setTimeout calls for same sandboxId without clearing previous ones. Add a Map to track timeouts and clearTimeout before creating new one. (2) Sentry.captureException in route.ts line 117 missing context - add extra field with projectId, sandboxId, model, and first 100 chars of prompt. (3) In streaming.ts schema, files field uses v.any - prefer v.record(v.string(), v.string()) for type safety. (4) In error-fixer.ts line 62 add explicit return await for better stack traces. |
|
POSITIVE ASPECTS: This PR shows excellent architectural decisions: (1) Sentry integration is comprehensive with breadcrumbs throughout the agent flow (2) Clean separation between agents, tools, and sandbox management (3) Strong type safety using TypeScript and Zod schemas for AI tool inputs (4) Robust retry logic with exponential backoff and configurable retry conditions (5) Efficient SSE streaming without buffering entire responses (6) Smart resource management with 5-minute sandbox connection caching. The migration from Inngest to direct execution simplifies the stack significantly. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
TESTING RECOMMENDATIONS: No tests were added for this major architectural change. Critical test coverage needed: (1) Unit tests for src/agents/retry.ts testing exponential backoff, max attempts, and retry conditions (2) Integration tests for src/app/api/generate/route.ts testing SSE streaming, client disconnects, and error responses (3) Security tests for src/agents/tools.ts verifying command injection prevention in listFiles tool (4) Error handling tests for validation and error-fixer flows including max retry limits and error propagation. |
- Introduced `taskStatusEnum` and `taskStageEnum` to standardize task status and stage values. - Updated `taskProgress` schema to use the new enums for `status` and `stage`. - Modified `updateProgress` and `getProgress` mutations to utilize the new enums for better type safety. - Improved error handling and logging in various components for enhanced debugging and clarity. - Added validation for `sandboxId` in the API to ensure proper format and existence. This update streamlines task management and improves overall code maintainability.
CodeCapy Review ₍ᐢ•(ܫ)•ᐢ₎Codebase SummaryZapDev is an AI-powered development platform for creating web applications through real-time interactions with AI agents using sandboxed environments. The platform integrates Next.js, React, Convex, and a variety of agent tools to generate, validate, and deploy code based on user prompts. PR ChangesThis pull request migrates the AI code generation workflow from using Inngest-based background job orchestration to a new direct agent-based architecture. Key changes include the removal of Inngest client and associated API routes, addition of a new agent infrastructure with modules for code generation, error fixing, framework selection, and validation, and the introduction of server-sent events (SSE) for real-time progress streaming. The API endpoint for triggering AI tasks has been updated from '/api/inngest/trigger' to '/api/generate'. Additionally, UI components (ProjectForm and MessageForm) have been updated to use the new endpoint. Setup Instructions
Generated Test Cases1: AI Code Generation Real-Time Streaming Test ❗️❗️❗️Description: Verifies that when a user submits a generation prompt through the UI, the client connects to the /api/generate SSE endpoint and receives real-time status updates and a complete code generation summary. Prerequisites:
Steps:
Expected Result: The SSE stream starts upon submission, real-time status updates are visible, and ultimately a complete summary is provided along with the preview of generated files. No error messages should be displayed if the process succeeds. 2: Project Form Submission Endpoint Test ❗️❗️❗️Description: Ensures that when a new project form is submitted, the form triggers a POST request to the new '/api/generate' endpoint with the correct payload and displays real-time updates. Prerequisites:
Steps:
Expected Result: The form submission successfully sends the proper payload to '/api/generate', and the UI receives and displays SSE progress messages including a final summary. The old Inngest trigger should not be called. 3: Message Form Submission Endpoint Test ❗️❗️❗️Description: Checks that the message form in the messaging interface uses the new '/api/generate' endpoint replacing the old Inngest trigger, and correctly streams responses. Prerequisites:
Steps:
Expected Result: The message form uses the correct endpoint with the proper payload, and SSE stream events display status updates ending with the final result update. The UI reflects the successful submission without errors. 4: Error Handling and Notification Test ❗️❗️❗️Description: Verifies that if the code generation process fails (e.g., due to invalid sandboxId or network error), the UI displays a clear error message via the SSE stream. Prerequisites:
Steps:
Expected Result: The UI displays an error message (e.g., 'Automatic fix failed. Please review the sandbox and try again.' or a custom error message) when the /api/generate endpoint returns an error event, and the streaming connection closes after error handling. 5: UI Consistency After Migration Test ❗️❗️Description: Ensures that UI elements such as the project form, message form, and polar checkout button remain visually consistent and functional after migration to the new agent architecture. Prerequisites:
Steps:
Expected Result: The UI elements remain consistent with the design system (using Shadcn/ui components) and no layout or visual regressions are present. All buttons and forms should be responsive and error-free. Raw Changes AnalyzedFile: .cursor/rules/convex_rules.mdc
Changes:
@@ -198,7 +198,7 @@ export const exampleQuery = query({
handler: async (ctx, args) => {
const idToUsername: Record<Id<"users">, string> = {};
for (const userId of args.userIds) {
- const user = await ctx.db.get(userId);
+ const user = await ctx.db.get("users", userId);
if (user) {
idToUsername[user._id] = user.username;
}
@@ -236,8 +236,8 @@ const messages = await ctx.db
## Mutation guidelines
-- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist.
-- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist.
+- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.replace('tasks', taskId, { name: 'Buy milk', completed: false })`
+- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.patch('tasks', taskId, { completed: true })`
## Action guidelines
- Always add `"use node";` to the top of files containing actions that use Node.js built-in modules.
@@ -307,7 +307,7 @@ export const exampleQuery = query({
args: { fileId: v.id("_storage") },
returns: v.null(),
handler: async (ctx, args) => {
- const metadata: FileMetadata | null = await ctx.db.system.get(args.fileId);
+ const metadata: FileMetadata | null = await ctx.db.system.get("_storage", args.fileId);
console.log(metadata);
return null;
},
@@ -434,7 +434,7 @@ Internal Functions:
"description": "This example shows how to build a chat app without authentication.",
"version": "1.0.0",
"dependencies": {
- "convex": "^1.17.4",
+ "convex": "^1.31.2",
"openai": "^4.79.0"
},
"devDependencies": {
@@ -667,6 +667,35 @@ export default defineSchema({
});+#### convex/tsconfig.json
src/App.tsxexport default function App() {
File: AGENTS.md
Changes:
@@ -31,7 +31,6 @@ bun run test # Run Jest tests (if configured)
# Build E2B templates for AI code generation (requires Docker)
cd sandbox-templates/[framework] # nextjs, angular, react, vue, or svelte
e2b template build --name your-template-name --cmd "/compile_page.sh"
-# Update template name in src/inngest/functions.ts after buildingArchitecture Overview@@ -40,47 +39,61 @@ e2b template build --name your-template-name --cmd "/compile_page.sh"
Core ArchitectureAI-Powered Code Generation Flow
Data Flow
Directory StructureKey Components@@ -92,13 +105,14 @@ sandbox-templates/ # E2B sandbox templates for each framework
-Inngest Functions ( Code Standards for AI Agents
E2B TemplatesBefore running AI code generation:
Convex Development
Code Generation Failures
Build or Lint Failures in Sandbox
File: convex/_generated/api.d.ts @@ -36,6 +37,7 @@ declare const fullApi: ApiFromModules<{
File: convex/_generated/dataModel.d.ts
File: convex/schema.ts +export const taskStatusEnum = v.union(
+export const taskStageEnum = v.union(
export default defineSchema({
File: convex/streaming.ts
+export const getProgress = query({
+export const deleteProgress = mutation({
File: package.json
File: src/agents/agents/code-generation.ts
+interface GenerationResult {
+export async function generateCode(
+function extractSummary(text: string): string {
File: src/agents/agents/error-fixer.ts
File: src/agents/agents/framework-selector.ts
File: src/agents/agents/validation.ts
File: src/agents/client.ts
+export const openrouter = createOpenAI({
+export const MODEL_CONFIGS = {
+export type ModelId = keyof typeof MODEL_CONFIGS;
File: src/agents/index.ts File: src/agents/logger.ts
+export function createLogger(taskId: string, extra?: Record<string, unknown>): AgentLogger {
File: src/agents/prompts/index.ts
+const FRAMEWORK_PROMPTS: Record<Framework, string> = {
+export function getFrameworkPrompt(framework: Framework): string {
File: src/agents/retry.ts
+const DEFAULT_OPTIONS: Required = {
+function sleep(ms: number): Promise {
+export async function withRetry(
+export const retryOnRateLimit = (error: Error): boolean => {
+export const retryOnTimeout = (error: Error): boolean => {
+export const retryOnTransient = (error: Error): boolean => {
File: src/agents/sandbox.ts
+export class SandboxManager {
+export const sandboxManager = SandboxManager.getInstance(); File: src/agents/tools.ts
+export type AgentTools = ReturnType; File: src/agents/types.ts
+export type TaskStatus = 'pending' | 'running' | 'complete' | 'failed';
+export interface GenerationRequest {
+export interface ConversationMessage {
+export interface ValidationResult {
+export interface StreamUpdate {
+export interface FileWriteResult {
+export interface TerminalResult {
File: src/app/api/fix-errors/route.ts type FixErrorsRequestBody = {
File: src/app/api/generate/route.ts
+export async function POST(request: NextRequest) {
File: src/app/api/import/figma/direct/route.ts export async function POST(request: Request) {
@@ -40,43 +38,30 @@ export async function POST(request: Request) {
File: src/app/api/import/figma/process/route.ts export async function POST(request: Request) {
@@ -41,12 +36,13 @@ export async function POST(request: Request) {
@@ -74,21 +69,15 @@ export async function POST(request: Request) {
File: src/app/api/inngest/route.ts
|
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
Pull Request Review: Migrate off Inngest to New Agent ArchitectureThis is a significant architectural refactoring that removes Inngest job orchestration in favor of a direct SSE-based agent execution model. Overall, the implementation is well-structured with good separation of concerns. ✅ Strengths
🔴 Critical Issues1. Security: Sandbox ID Validation Bypass Risk (src/app/api/generate/route.ts:44-51) The regex validation for sandboxId is good, but consider:
2. Resource Leak: SSE Stream Not Closed on Early Exit (src/app/api/generate/route.ts:65-139) If the client disconnects during generation, the async IIFE continues running, potentially wasting resources. Add AbortController to detect disconnections. 3. Race Condition: Convex Client Not Thread-Safe (src/agents/agents/code-generation.ts:13-21, src/app/api/generate/route.ts:13-23) The lazy initialization pattern for ConvexHttpClient is duplicated in two files. Create a shared module src/lib/convex-client.ts and import it everywhere. 4. Potential Memory Leak: Sandbox Cache (src/agents/sandbox.ts:5-6, 85-100) Sandboxes are cached for 5 minutes, but with high volume, the cache could grow unbounded. Consider adding a maximum cache size with LRU eviction.
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/app/api/generate/route.ts (3)
85-97: Handle null project before accessing properties.The
projectquery result could benullif the project doesn't exist. While line 107 uses optional chaining with a fallback, earlier validation would provide a clearer error to the client.🔎 Proposed fix
const project = await convex.query(api.projects.getForSystem, { projectId: projectId as Id<'projects'>, }); + if (!project) { + await sendUpdate({ type: 'error', error: 'Project not found' }); + await writer.close(); + return; + } + const result = await generateCode(
119-129: Generation marked complete even when validation fails.After
fixErrorsruns, the code unconditionally marks the message asCOMPLETEand sends a completion event. If validation still fails after fix attempts, users receive a "complete" status for potentially broken code. Consider conditionally marking asFAILEDor including validation status in the response.🔎 Proposed fix
+ const finalStatus = validation.success ? 'COMPLETE' : 'FAILED'; + await convex.mutation(api.messages.updateMessage, { messageId: assistantMessageId, content: result.summary, - status: 'COMPLETE', + status: finalStatus, }); await sendUpdate({ - type: 'complete', + type: validation.success ? 'complete' : 'error', message: result.summary, files: result.files, + ...(validation.success ? {} : { error: 'Validation failed after fix attempts', validationErrors: validation.errors }), });
100-105: Check final validation state before marking generation as complete.The code unconditionally marks the message as
'COMPLETE'(line 122) regardless of whetherfixErrorssuccessfully resolved the errors. WhenfixErrorsreachesMAX_FIX_ATTEMPTS(2 attempts), it returns{ success: false }with an error message, but this failed state is never checked. The reassignedvalidationresult from line 104 should be verified before proceeding to mark the generation as successful.
🧹 Nitpick comments (6)
src/app/api/generate/route.ts (1)
130-138: Error handling captures exceptions but consider writer error handling.The
finallyblock ensures the writer is closed, which is correct. However, ifwriter.close()itself throws, the error won't be captured. Consider wrapping the close in a try-catch.🔎 Proposed fix
} finally { - await writer.close(); + try { + await writer.close(); + } catch (closeError) { + Sentry.captureException(closeError); + } }src/agents/client.ts (1)
46-48: Fallback ingetModelis unreachable due to TypeScript typing.The
MODEL_CONFIGS[modelId]lookup with fallback|| MODEL_CONFIGS['auto']suggests runtime protection, but sincemodelIdis typed asModelId(a key ofMODEL_CONFIGS), the fallback will never trigger at runtime if TypeScript is enforced. This is fine for type safety but the fallback is dead code.If you want to keep the fallback for robustness against runtime type violations (e.g., from external input), consider:
-export function getModel(modelId: ModelId) { +export function getModel(modelId: string) { const config = MODEL_CONFIGS[modelId] || MODEL_CONFIGS['auto']; + // TypeScript: modelId might be an unrecognized string at runtime return openrouter(config.id); }Alternatively, remove the fallback if you're confident in type safety:
export function getModel(modelId: ModelId) { - const config = MODEL_CONFIGS[modelId] || MODEL_CONFIGS['auto']; + const config = MODEL_CONFIGS[modelId]; return openrouter(config.id); }src/agents/logger.ts (2)
32-45: Format string vulnerability addressed, but inconsistent style.The
warn()method now safely concatenates data viaJSON.stringifybefore passing toconsole.warn. This addresses the CodeQL warning. However, consider aligning the logging style withinfo()which uses structured object logging for better consistency and log parsing.🔎 Optional: Align with info() structured logging style
warn(message: string, data?: Record<string, unknown>): void { - const logMessage = `[${this.taskId}] WARN: ${message}`; - const fullMessage = data - ? `${logMessage} ${JSON.stringify(data)}` - : logMessage; - console.warn(fullMessage); + console.warn({ + taskId: this.taskId, + level: 'warn', + message, + ...(data && { data }), + }); Sentry.addBreadcrumb({
7-15: Consider Sentry tag collision in concurrent scenarios.
Sentry.setTagandSentry.setContextset global scope attributes. If multipleAgentLoggerinstances run concurrently (e.g., parallel task processing), they'll overwrite each other's tags, potentially attributing errors to the wrong task.For production resilience, consider using
Sentry.withScope()or passing the scope explicitly tocaptureExceptioncalls instead of relying on global state.src/agents/agents/error-fixer.ts (1)
41-50: Consider adding error handling for sandbox and streaming operations.The function doesn't catch errors from
sandboxManager.connect(),streamText(), or the streaming loop. If the sandbox connection fails or the AI model errors, the exception propagates without structured logging or a graceful failure response.Consider wrapping critical operations in try/catch to log errors via the
loggerand return a structuredValidationResultwith the error:🔎 Proposed error handling pattern
+ let sandbox; + try { + sandbox = await sandboxManager.connect(sandboxId); + } catch (err) { + logger.error(err instanceof Error ? err : String(err), { phase: 'connect' }); + return { success: false, errors: [`Sandbox connection failed: ${err}`] }; + } - const sandbox = await sandboxManager.connect(sandboxId); const tools = createTools(sandbox);src/agents/tools.ts (1)
72-76: Consider more robust command blocking and exit code handling.The dev server blocklist uses simple string matching that could be bypassed (e.g.,
npm run devwith extra spaces,npx serve,node server.js). Since this runs in a sandbox, the risk is mitigated, but consider regex patterns for stricter matching.Also, defaulting
exitCodeto0(line 83) could mask actual failures if the sandbox API returnsundefinedon error. Consider defaulting to a non-zero value or logging when undefined.Also applies to: 83-83
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
convex/schema.tsconvex/streaming.tssrc/agents/agents/error-fixer.tssrc/agents/client.tssrc/agents/logger.tssrc/agents/sandbox.tssrc/agents/tools.tssrc/agents/types.tssrc/app/api/generate/route.tssrc/app/api/import/figma/direct/route.tssrc/app/api/import/figma/process/route.tssrc/components/polar-checkout-button.tsxsrc/lib/polar-client.ts
✅ Files skipped from review due to trivial changes (2)
- src/components/polar-checkout-button.tsx
- src/lib/polar-client.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- convex/schema.ts
- src/agents/sandbox.ts
- src/agents/types.ts
- convex/streaming.ts
- src/app/api/import/figma/direct/route.ts
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/app/api/generate/route.tssrc/agents/client.tssrc/agents/logger.tssrc/agents/agents/error-fixer.tssrc/app/api/import/figma/process/route.tssrc/agents/tools.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification
Files:
src/app/api/generate/route.tssrc/app/api/import/figma/process/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/app/api/generate/route.tssrc/agents/client.tssrc/agents/logger.tssrc/agents/agents/error-fixer.tssrc/app/api/import/figma/process/route.tssrc/agents/tools.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/app/api/generate/route.tssrc/agents/agents/error-fixer.tssrc/agents/tools.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/**/*.{ts,tsx} : Use modern framework patterns: Next.js App Router and React hooks
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/app/api/**/*.ts : Sync credit usage with Clerk custom claim `plan: 'pro'` for Pro tier verification
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/middleware.ts : Use Clerk authentication middleware in `src/middleware.ts` for end-to-end type safety
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/schema.ts : Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in `usage` table with rolling 24-hour expiration
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always use the `v.null()` validator when returning a null value from a Convex function
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/app/api/generate/route.tssrc/agents/tools.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/auth.config.ts : Keep OAuth tokens encrypted in Convex; never expose API keys in client-side code (use NEXT_PUBLIC_ prefix only for public values)
Applied to files:
src/agents/client.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/instrumentation.ts : Leverage Sentry integration (`src/instrumentation.ts`) for production error tracking and OpenTelemetry for distributed tracing
Applied to files:
src/agents/logger.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/error-fixer.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/agents/agents/error-fixer.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `api` object from `convex/_generated/api.ts` to call public functions registered with `query`, `mutation`, or `action`
Applied to files:
src/app/api/import/figma/process/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `internal` object from `convex/_generated/api.ts` to call internal functions registered with `internalQuery`, `internalMutation`, or `internalAction`
Applied to files:
src/app/api/import/figma/process/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `query`, `mutation`, and `action` to register public functions that are exposed to the public Internet
Applied to files:
src/app/api/import/figma/process/route.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/tools.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/**/*.ts : Validate all user inputs using Zod schemas in prompts; sanitize file paths to prevent directory traversal attacks
Applied to files:
src/agents/tools.ts
🧬 Code graph analysis (4)
src/agents/logger.ts (1)
src/agents/index.ts (2)
AgentLogger(8-8)createLogger(8-8)
src/agents/agents/error-fixer.ts (6)
src/agents/types.ts (2)
StreamUpdate(46-53)ValidationResult(40-44)src/agents/logger.ts (1)
createLogger(78-80)src/agents/sandbox.ts (1)
sandboxManager(140-140)src/agents/tools.ts (1)
createTools(6-109)src/agents/client.ts (1)
getModel(46-49)src/agents/agents/validation.ts (1)
runValidation(5-35)
src/app/api/import/figma/process/route.ts (3)
types/convex-extended-api.d.ts (1)
api(10-20)convex/_generated/api.js (2)
api(21-21)api(21-21)convex/_generated/dataModel.d.ts (1)
Id(48-49)
src/agents/tools.ts (3)
src/agents/index.ts (2)
createTools(9-9)AgentTools(10-10)test-e2b-sandbox.js (1)
sandbox(31-31)src/agents/logger.ts (1)
error(47-60)
🪛 GitHub Check: CodeQL
src/agents/tools.ts
[failure] 94-94: Incomplete string escaping or encoding
This does not escape backslash characters in the input.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Codacy Security Scan
- GitHub Check: claude-review
🔇 Additional comments (18)
src/app/api/generate/route.ts (3)
12-23: LGTM! Lazy initialization pattern for Convex client.The lazy initialization pattern correctly handles the case where the environment variable might not be set at build time, deferring the error to runtime when the client is actually needed.
25-51: Pro tier verification and sandboxId validation implemented correctly.The implementation now properly extracts
sessionClaims, checks forplan === 'pro', and validatessandboxIdwith both presence and format checks. This addresses the previous review feedback.
141-148: LGTM! SSE response headers are properly configured.The response includes all necessary headers for Server-Sent Events:
Content-Type,Cache-Control,Connection, andX-Accel-Bufferingfor nginx compatibility.src/agents/client.ts (1)
3-10: LGTM! Proper runtime validation for required API key.The validation now provides a clear, actionable error message with guidance on where to obtain an API key. This addresses the previous review feedback.
src/app/api/import/figma/process/route.ts (4)
5-5: LGTM! Proper type import for Convex Id.Adding the explicit
Idtype import from the generated data model improves type safety for theprojectIdcast.
28-30: LGTM! Improved OAuth connection typing.The explicit typing of
connection as { accessToken: string }provides better type safety and aligns with the API's expected shape. This replaces the previous(api as any)casting approach.Also applies to: 39-45
54-70: LGTM! Enhanced import record with metadata.The import record now includes
sourceUrland detailedfigmaFileDatametadata, providing richer context for the import. The type assertion forfileDatais reasonable given the Figma API response structure.
72-80: 503 response correctly signals temporary unavailability.This addresses the previous critical issue by returning an appropriate error status instead of a misleading success. The response clearly communicates that the feature is unavailable during migration.
Consider adding a tracking mechanism or TODO comment to ensure this is re-enabled when the new agent architecture supports Figma processing:
+ // TODO: Implement Figma processing with new agent architecture + // Track: https://github.com/Jackson57279/zapdev/issues/XXX return NextResponse.json( { success: false,src/agents/logger.ts (3)
17-30: Format string vulnerability addressed.The
info()method now uses structured object logging (console.log({ taskId, message, data })) instead of passingdataas a separate format argument. This addresses the CodeQL warning from previous reviews.
47-60: LGTM with minor redundancy.The error handling correctly converts string inputs to Error objects and uses structured logging. The
instanceof Errorcheck on line 52 is technically redundant sinceerris always an Error after line 48, but it's harmless and could be defensive if the type coercion logic changes.
62-75: LGTM!The
progress(),complete(), andstartSpan()methods are well-implemented. The duration tracking and Sentry measurement integration provide good observability for task lifecycle monitoring.src/agents/agents/error-fixer.ts (3)
44-57: Tool calls await addressed.The code now correctly awaits both
result.textandresult.toolCalls(lines 56-57), ensuring tool executions (like file writes) complete before running validation. This addresses the concern from the previous review.
30-36: LGTM!The retry logic correctly bounds recursion to
MAX_FIX_ATTEMPTS(2 attempts) with the guard at function entry. This aligns with the documented requirement for "max 2 retry attempts on lint/build errors." The recursive pattern is clear and safe.Based on learnings, this matches the expected auto-fix retry behavior.
Also applies to: 62-64
9-18: LGTM!The error-fixing prompt is clear, actionable, and appropriately scoped. It instructs the model to read, understand, fix, and verify - a good chain-of-thought approach for automated debugging.
src/agents/tools.ts (4)
8-32: LGTM!The
createOrUpdateFilestool correctly writes files to the sandbox with proper Sentry breadcrumb logging and invokes the optionalonFileWritecallback. The implementation is clean and handles the file iteration properly.
34-57: LGTM!The
readFilestool has appropriate per-file error handling. Including the error message in the output is reasonable here since it helps the AI agent understand read failures.
99-106: LGTM!The output parsing correctly uses null-byte separation (
-print0+split('\0')) to safely handle filenames with spaces or newlines. The 50-file limit is enforced via slice rather thanhead, which is safer since-print0output doesn't work well withhead.
111-111: LGTM!The
AgentToolstype export is clean and enables type-safe consumption of the tools surface by downstream modules.
- Fix convex/streaming.ts: Change v.optional() to v.union(v.null(), v.object(...)) for getProgress query return type (TypeScript error) - Update eslint.config.mjs: Migrate to Next.js 16 flat config format with direct imports from eslint-config-next - Update package.json: Change lint script from 'next lint' to 'eslint .' (next lint removed in Next.js 16) - Remove unused imports in multiple files: - convex/sandboxSessions.ts: Remove unused api and Id imports - convex/rateLimit.ts: Remove unused 'now' variable - src/app/api/import/figma/callback/route.ts: Remove unused fetchQuery - src/app/api/import/github/callback/route.ts: Remove unused fetchQuery - src/app/layout.tsx: Remove unused Clerk component imports - Fix unused variable warnings with underscore prefix convention Co-authored-by: openhands <openhands@all-hands.dev>
CodeCapy Review ₍ᐢ•(ܫ)•ᐢ₎Codebase SummaryZapDev is an AI-powered development platform that lets users create web applications in real-time through conversational interactions with AI agents running in isolated sandboxes. It integrates Next.js, React, TypeScript, and Convex for backend management, along with Clerk for authentication. Users can send code generation prompts, view real-time streaming updates via SSE, manage generated files through a file explorer, and interact with AI agents directly. PR ChangesThis PR migrates the background job orchestration from Inngest to a direct agent execution model using a new agent architecture. Key changes include removal of Inngest functions and endpoints, replacement of '/api/inngest/trigger' with a new '/api/generate' endpoint that streams real-time updates via SSE, updates to API endpoints and schema for task progress tracking, and modifications to frontend components (project and message forms) to trigger the new API workflow. Setup InstructionsTo set up the test environment:
Generated Test Cases1: AI Code Generation Streaming Test ❗️❗️❗️Description: Tests that a Pro user submitting a generation prompt receives real-time streaming updates from the new /api/generate endpoint. Prerequisites:
Steps:
Expected Result: The user sees real-time updates (e.g., status messages like 'Initializing AI agent…', 'Generating code…', 'Validating code…', and a final 'complete' event with summary text and generated files) delivered via SSE and the UI updates to reflect these messages. 2: Project and Message Form Submission Test ❗️❗️❗️Description: Verifies that when a user submits a new project creation message or chat prompt, the updated forms call the new API endpoint (/api/generate) for AI processing and display results correctly. Prerequisites:
Steps:
Expected Result: The form submission successfully initiates an AI code generation task via '/api/generate', the UI shows loading/spinner indicators, and eventually the generated code summary and file overview appear in the UI. 3: Error Handling in Code Generation Streaming ❗️❗️❗️Description: Tests how errors during the code generation process are communicated to the user through SSE stream updates. Prerequisites:
Steps:
Expected Result: The user’s SSE stream includes an error update with a message such as 'Unknown error' or a more descriptive error message, and the UI should reflect that the code generation process has failed. 4: UI Layout and Visual Integrity Test ❗️❗️Description: Ensures that changes to the project and message forms (including removal of old Inngest triggers) do not negatively impact the layout, navigation, and overall visual appearance of the UI. Prerequisites:
Steps:
Expected Result: The overall UI layout remains consistent with previous designs. All components render correctly without misalignment, unexpected spacing issues, or broken elements, and the new streaming notifications blend seamlessly with the existing style. 5: Data Persistence Verification Test ❗️❗️❗️Description: Checks that user-generated data (e.g., project details, generated summaries, uploaded files) are correctly persisted in the backend after successful AI code generation. Prerequisites:
Steps:
Expected Result: The generated summary and file details are stored in the system and remain visible after navigation or refresh, demonstrating that the new agent-based workflow correctly persists user data to the backend. Raw Changes AnalyzedFile: .cursor/rules/convex_rules.mdc
Changes:
@@ -198,7 +198,7 @@ export const exampleQuery = query({
handler: async (ctx, args) => {
const idToUsername: Record<Id<"users">, string> = {};
for (const userId of args.userIds) {
- const user = await ctx.db.get(userId);
+ const user = await ctx.db.get("users", userId);
if (user) {
idToUsername[user._id] = user.username;
}
@@ -236,8 +236,8 @@ const messages = await ctx.db
## Mutation guidelines
-- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist.
-- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist.
+- Use `ctx.db.replace` to fully replace an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.replace('tasks', taskId, { name: 'Buy milk', completed: false })`
+- Use `ctx.db.patch` to shallow merge updates into an existing document. This method will throw an error if the document does not exist. Syntax: `await ctx.db.patch('tasks', taskId, { completed: true })`
## Action guidelines
- Always add `"use node";` to the top of files containing actions that use Node.js built-in modules.
@@ -307,7 +307,7 @@ export const exampleQuery = query({
args: { fileId: v.id("_storage") },
returns: v.null(),
handler: async (ctx, args) => {
- const metadata: FileMetadata | null = await ctx.db.system.get(args.fileId);
+ const metadata: FileMetadata | null = await ctx.db.system.get("_storage", args.fileId);
console.log(metadata);
return null;
},
@@ -434,7 +434,7 @@ Internal Functions:
"description": "This example shows how to build a chat app without authentication.",
"version": "1.0.0",
"dependencies": {
- "convex": "^1.17.4",
+ "convex": "^1.31.2",
"openai": "^4.79.0"
},
"devDependencies": {
@@ -667,6 +667,35 @@ export default defineSchema({
});+#### convex/tsconfig.json
src/App.tsxexport default function App() {
File: AGENTS.md
Changes:
@@ -31,7 +31,6 @@ bun run test # Run Jest tests (if configured)
# Build E2B templates for AI code generation (requires Docker)
cd sandbox-templates/[framework] # nextjs, angular, react, vue, or svelte
e2b template build --name your-template-name --cmd "/compile_page.sh"
-# Update template name in src/inngest/functions.ts after buildingArchitecture Overview@@ -40,47 +39,61 @@ e2b template build --name your-template-name --cmd "/compile_page.sh"
Core ArchitectureAI-Powered Code Generation Flow
Data Flow
Directory StructureKey Components@@ -92,13 +105,14 @@ sandbox-templates/ # E2B sandbox templates for each framework
-Inngest Functions ( Code Standards for AI Agents
E2B TemplatesBefore running AI code generation:
Convex Development
Code Generation Failures
Build or Lint Failures in Sandbox
File: convex/_generated/api.d.ts @@ -36,6 +37,7 @@ declare const fullApi: ApiFromModules<{
File: convex/_generated/dataModel.d.ts
File: convex/rateLimit.ts
File: convex/sandboxSessions.ts /**
File: convex/schema.ts +export const taskStatusEnum = v.union(
+export const taskStageEnum = v.union(
export default defineSchema({
File: convex/streaming.ts
+export const getProgress = query({
+export const deleteProgress = mutation({
File: eslint.config.mjs
|
|
🚀 Launching Scrapybara desktop... |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/modules/projects/ui/components/message-form.tsx (1)
157-169: MissingonStreamEndcall in error catch block.If an error occurs after
onStreamStartis called but before anyonStreamEnd(e.g., network failure during streaming), the streaming state in the parent component will remain stuck asisStreaming: true.🔎 Proposed fix: Ensure cleanup in catch block
} catch (error) { + onStreamEnd?.(); if (error instanceof Error) { toast.error(error.message); if (error.message.includes("credits") || error.message.includes("out of credits")) { router.push("/pricing"); } } else { toast.error("Something went wrong"); } } finally { setIsCreating(false); }Alternatively, move
onStreamEndto thefinallyblock with a guard:+ let streamStarted = false; // ... in onSubmit ... - onStreamStart?.(); + onStreamStart?.(); + streamStarted = true; // ... } finally { + if (streamStarted) { + onStreamEnd?.(); + } setIsCreating(false); }package.json (1)
14-106: Fix orphaned imports after Inngest removal.The Inngest dependency was removed from
package.json, but three test files still contain imports from the non-existentsrc/inngest/module:
tests/security.test.tsimportsisValidFilePathfrom../src/inngest/functionstests/model-selection.test.tsimportsselectModelForTask,MODEL_CONFIGSfrom../src/inngest/functionsandFrameworkfrom../src/inngest/typestests/file-operations.test.tsimportsreadFileWithTimeout,readFilesInBatchesfrom../src/inngest/functionsEither remove these imports and update the tests, or recreate the
src/inngest/module with the utility functions these tests depend on.
♻️ Duplicate comments (19)
src/agents/agents/validation.ts (1)
10-10: Update validation commands to usebuninstead ofnpm.The validation commands still use
npm run lintandnpm run build, but the project's coding guidelines and learnings explicitly require usingbunfor all package management and script execution. Update these commands tobun run lintandbun run build.Based on learnings, all sandbox operations should use bun as the package manager.
🔎 Proposed fix
- const lintResult = await sandboxManager.runCommand(sandbox, 'npm run lint -- --cache', 30000); + const lintResult = await sandboxManager.runCommand(sandbox, 'bun run lint -- --cache', 30000);- const buildResult = await sandboxManager.runCommand(sandbox, 'npm run build', 120000); + const buildResult = await sandboxManager.runCommand(sandbox, 'bun run build', 120000);Also applies to: 22-22
src/agents/sandbox.ts (1)
155-158: DefaultingexitCodeto 0 may mask command failures.If
result.exitCodeisundefined, returning0implies success and could hide cases where the command failed but didn't set an exit code. Consider preservingundefinedor defaulting to a non-zero sentinel value.🔎 Proposed fix
return { stdout: result.stdout || '', stderr: result.stderr || '', - exitCode: result.exitCode ?? 0, + exitCode: result.exitCode ?? -1, };src/agents/agents/code-generation.ts (1)
60-62: Await onProgress callback in file write handler.The
onProgresscallback returns aPromisebut isn't awaited in thecreateToolscallback at line 61. This could cause unhandled rejections or race conditions with subsequent progress updates.🔎 Proposed fix
- const tools = createTools(sandbox, (path) => { - onProgress({ type: 'file', filePath: path }); + const tools = createTools(sandbox, async (path) => { + await onProgress({ type: 'file', filePath: path }); });Note: This requires updating the
onFileWritetype increateToolsto accept(path: string) => void | Promise<void>and awaiting it in the tool execution.src/app/api/generate/route.ts (3)
26-62: Add Pro tier verification for credit usage.Per coding guidelines, API routes should sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification. The current implementation checks credits but doesn't verify the user's plan tier. This could lead to inconsistent credit enforcement.As per coding guidelines, sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification.🔎 Proposed fix
export async function POST(request: NextRequest) { - const { userId } = await auth(); + const { userId, sessionClaims } = await auth(); if (!userId) { return new Response('Unauthorized', { status: 401 }); } + // Verify Pro tier for credit usage + const isPro = sessionClaims?.plan === 'pro'; + if (!isPro) { + return new Response( + JSON.stringify({ error: 'Pro subscription required' }), + { status: 402, headers: { 'Content-Type': 'application/json' } } + ); + } + const convex = getConvex();
128-130: Handle null project before accessing properties.The
projectquery result could benullif the project doesn't exist. While line 142 uses optional chaining, the code should validate that the project exists and return a clear error to the client.🔎 Proposed fix
const project = await convex.query(api.projects.getForSystem, { projectId: projectId as Id<'projects'>, }); + + if (!project) { + await sendUpdate({ type: 'error', error: 'Project not found' }); + await writer.close(); + return; + } + console.log('[GENERATE] Project framework:', project?.framework);
186-203: Mark generation as failed when validation doesn't pass.After validation and auto-fix attempts (lines 172-181), the handler always creates a fragment and updates the message to
COMPLETE(lines 186-203), even whenvalidation.successmight still befalse. If lint/build fail after all fix attempts, the user receives a "complete" event and stored code that never built, masking the failure.🔎 Proposed fix
if (hasFiles) { await sendUpdate({ type: 'status', message: 'Validating code...' }); let validation = await runValidation(effectiveSandboxId); if (!validation.success) { console.log('[GENERATE] Validation failed, attempting fixes'); await sendUpdate({ type: 'status', message: 'Fixing errors...' }); validation = await fixErrors(effectiveSandboxId, validation.errors || [], 0, sendUpdate); + + if (!validation.success) { + // Still failed after fixes + await sendUpdate({ + type: 'error', + error: 'Code generation failed validation after fix attempts', + }); + await convex.mutation(api.messages.updateMessage, { + messageId: assistantMessageId, + content: 'Failed to generate valid code', + status: 'ERROR', + }); + return; + } } else { console.log('[GENERATE] Validation passed'); } }src/modules/projects/ui/components/message-form.tsx (1)
65-65: The Gemini model identifier stability concern was previously flagged.As noted in a prior review,
"google/gemini-3-pro-preview"is supported by OpenRouter but has reported stability issues. This is a pre-existing concern documented in past review comments.package.json (2)
17-17: Invalid package versions will fail installation.As flagged in previous review:
@ai-sdk/openaiv3.0.1 doesn't exist (latest is 2.0.24), andaiv6.0.3 doesn't exist (latest stable is 5.0.116, with 6.0.0-beta.* in prerelease). Correct these versions before merging.Also applies to: 64-64
123-123: js-yaml override pins a vulnerable version; upgrade to 4.1.1+.As flagged in previous review: Version 4.1.0 contains a prototype-pollution vulnerability that was patched in 4.1.1. Change
"js-yaml": "^4.1.0"to"js-yaml": "^4.1.1".src/app/api/stripe/portal/route.ts (1)
7-15: Duplicate Stripe client initialization.This duplicates the
getStripe()factory fromsrc/lib/stripe/index.ts. Import from the shared module instead.🔎 Proposed fix
import { auth } from "@clerk/nextjs/server"; import { NextResponse } from "next/server"; -import Stripe from "stripe"; import { ConvexHttpClient } from "convex/browser"; import { api } from "@/convex/_generated/api"; +import { getStripe } from "@/lib/stripe"; -let _stripe: Stripe | null = null; -function getStripe(): Stripe { - if (!_stripe) { - const key = process.env.STRIPE_SECRET_KEY; - if (!key) throw new Error("STRIPE_SECRET_KEY is not configured"); - _stripe = new Stripe(key, { apiVersion: "2025-12-15.clover" }); - } - return _stripe; -}src/app/(home)/pricing/page-content.tsx (2)
13-39: PLANS constant is duplicated.This duplicates the
PLANSdefinition fromsrc/lib/stripe/index.ts. However, note that importing fromsrc/lib/stripe/index.tsdirectly won't work because it usesprocess.env.STRIPE_PRO_PRICE_ID(server-side) while this client component needsNEXT_PUBLIC_STRIPE_PRO_PRICE_ID.Consider creating a separate client-safe PLANS export or moving the client-facing PLANS to a shared location that uses the
NEXT_PUBLIC_prefixed env var.
222-237: UsePLANS.PRO.priceIdinstead of direct env var access.The direct
process.env.NEXT_PUBLIC_STRIPE_PRO_PRICE_IDaccess is redundant since you already define it inPLANS.PRO.priceIdon line 27.🔎 Proposed fix
<Button className="w-full" - onClick={() => handleCheckout(process.env.NEXT_PUBLIC_STRIPE_PRO_PRICE_ID || "")} - disabled={isCheckoutLoading || !process.env.NEXT_PUBLIC_STRIPE_PRO_PRICE_ID} + onClick={() => handleCheckout(PLANS.PRO.priceId)} + disabled={isCheckoutLoading || !PLANS.PRO.priceId} >src/app/api/stripe/checkout/route.ts (2)
7-15: Duplicate Stripe client initialization.Same as in
portal/route.ts- import fromsrc/lib/stripeinstead of duplicating the factory.
97-106: Inconsistent URL fallback handling.Line 100 uses
process.env.NEXT_PUBLIC_APP_URLwithout a fallback, while line 106 provides a fallback. DefinebaseUrlonce before the conditional.🔎 Proposed fix
+ const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"; + if (activeSubscription) { const portalSession = await stripe.billingPortal.sessions.create({ customer: stripeCustomerId, - return_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`, + return_url: `${baseUrl}/pricing`, }); return NextResponse.json({ url: portalSession.url }); } - const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";src/app/api/webhooks/stripe/route.ts (3)
7-15: Duplicate Stripe client initialization.This is the third location with a local
getStripe(). Import fromsrc/lib/stripeto consolidate.
27-33: Unknown price IDs silently default to "Free".If a new price ID is added to Stripe but not configured in
STRIPE_PRO_PRICE_ID, users will incorrectly show as "Free" plan. Add a warning log for unknown price IDs.🔎 Proposed fix
function getPlanNameFromPriceId(priceId: string): string { const proPriceId = process.env.STRIPE_PRO_PRICE_ID; if (priceId === proPriceId) { return "Pro"; } + if (priceId) { + console.warn(`Unknown price ID: ${priceId}. Defaulting to Free.`); + } return "Free"; }
100-109: Silent event dropping may hide issues.When the customer is deleted or
userIdis missing, the code logs an error but returns success. Consider returning early with a descriptive response or using warning-level logs to distinguish expected skips from unexpected failures.src/lib/stripe/index.ts (2)
36-38: EmptypriceIdfallback may cause checkout failures.If
STRIPE_PRO_PRICE_IDis not set, the empty string fallback will cause Stripe API errors during checkout. Consider leaving it asundefinedand validating at usage sites.🔎 Proposed fix
PRO: { name: "Pro", - priceId: process.env.STRIPE_PRO_PRICE_ID || "", + priceId: process.env.STRIPE_PRO_PRICE_ID, price: 29,Then add runtime validation where checkout is initiated.
101-105: Metadata storescustomerIdinstead ofuserId.The checkout route (
src/app/api/stripe/checkout/route.ts) storesuserIdin subscription metadata, but this function storescustomerId. This inconsistency could cause issues when processing webhooks. Standardize onuserId.🔎 Proposed fix
export async function createCheckoutSession( customerId: string, priceId: string, successUrl: string, - cancelUrl: string + cancelUrl: string, + userId?: string ): Promise<Stripe.Checkout.Session> { return await getStripe().checkout.sessions.create({ customer: customerId, mode: "subscription", payment_method_types: ["card"], line_items: [ { price: priceId, quantity: 1, }, ], success_url: successUrl, cancel_url: cancelUrl, subscription_data: { metadata: { - customerId, + userId: userId || customerId, }, }, }); }
🧹 Nitpick comments (9)
src/components/clerk-provider-wrapper.tsx (1)
10-12: Remove the wrapper or restructure it to follow Clerk v6.36.5 best practices.In Clerk v6.36.5,
ClerkProvideris designed to work directly inapp/layout.tsxwithout the"use client"directive. Since this wrapper component only passes children through without using any Clerk client hooks (likeuseUser,useAuth,UserButton), the"use client"boundary is unnecessary and prevents server-side rendering benefits.According to Clerk's guidance, server auth should be handled with
auth()orcurrentUser()helpers in Server Components, and"use client"should only be used in components that actually need Clerk's client-side hooks.Either:
- Remove this wrapper and place
ClerkProviderdirectly insrc/app/layout.tsx(Server Component, no"use client")- Or, if keeping the wrapper, add actual configuration (e.g.,
appearance,localization) and only use"use client"if it wraps components that call Clerk client hookssrc/app/(home)/subscription/page.tsx (1)
7-7: Remove unused import.The
useEffectimport is not used in this component. While it may be imported for future use, keeping unused imports can lead to confusion and slightly increases bundle size.🔎 Proposed fix
-import { useEffect } from "react";src/modules/projects/ui/components/messages-container.tsx (2)
97-101: Consider edge case: streaming content for wrong message.The current logic passes
streamingContentto any message wheremessage.status === "STREAMING" && isStreaming. If a new streaming message starts before the previous one completes, the accumulated content could briefly display on the wrong message.This is mitigated by
handleStreamStartclearing the content, but there's a brief race window. Consider keying the streaming content to a specific message ID:🔎 Optional: Key streaming content to message ID
- const [streamingContent, setStreamingContent] = useState(""); - const [isStreaming, setIsStreaming] = useState(false); + const [streamingContent, setStreamingContent] = useState(""); + const [streamingMessageId, setStreamingMessageId] = useState<string | null>(null); - const handleStreamStart = useCallback(() => { + const handleStreamStart = useCallback((messageId?: string) => { setStreamingContent(""); - setIsStreaming(true); + setStreamingMessageId(messageId ?? null); }, []); const handleStreamEnd = useCallback(() => { - setIsStreaming(false); + setStreamingMessageId(null); setStreamingContent(""); }, []);Then in the render:
streamingContent={ - message.status === "STREAMING" && isStreaming + message._id === streamingMessageId ? streamingContent : undefined }
70-70: Scroll effect may fire excessively during streaming.The effect triggers on every
streamingContentchange, which happens with each streamed chunk. This could cause performance issues with rapid updates. Consider debouncing or using a ref-based approach:🔎 Proposed optimization
useEffect(() => { if (messages) { bottomRef.current?.scrollIntoView(); } - }, [messages?.length, streamingContent]); + }, [messages?.length, isStreaming]);This scrolls when streaming starts/ends and when new messages arrive, rather than on every content chunk.
package.json (1)
1-13: Consider adding explicit packageManager field for bun.Based on coding guidelines, bun is the required package manager. Consider adding a
"packageManager": "bun@latest"field to enforce this and prevent accidental use of npm/yarn.🔎 Suggested addition
{ "name": "zapdev", "version": "0.1.0", "private": true, + "packageManager": "bun@latest", "scripts": {src/app/api/stripe/portal/route.ts (1)
17-25: Consider extracting shared Convex client factory.The
getConvex()pattern is duplicated acrossportal/route.ts,checkout/route.ts, andwebhooks/stripe/route.ts. Consider extracting to a shared utility (e.g.,src/lib/convex/index.ts).src/app/(home)/pricing/page-content.tsx (1)
153-168: Minor: Free plan button state could be misleading.When a signed-in user is on the Pro plan, the Free plan still shows "Current Plan" (line 166). Consider adjusting this to reflect the actual current plan state.
🔎 Proposed fix
) : ( - <Button variant="outline" className="w-full" disabled> - Current Plan + <Button variant="outline" className="w-full" disabled={isProUser}> + {isProUser ? "Free Tier" : "Current Plan"} </Button> )}src/app/api/webhooks/stripe/route.ts (2)
94-94: Unsafe type assertion bypasses type checking.Casting
event.data.objecttounknownthen toSubscriptionDatabypasses TypeScript's type safety. Consider using Stripe's built-in types or adding runtime validation.🔎 Proposed fix
- const subscription = event.data.object as unknown as SubscriptionData; + const subscription = event.data.object as Stripe.Subscription;Then update the
SubscriptionDatainterface to extend or matchStripe.Subscription, or remove it entirely and use Stripe's types directly.
114-120: Fallback period values may mask data issues.If
currentPeriodStartorcurrentPeriodEndare missing, the code silently uses fallback values. This could indicate a problem with the Stripe event data. Consider logging a warning when fallbacks are used.🔎 Proposed fix
const now = Date.now(); - const periodStart = subscription.currentPeriodStart - ? subscription.currentPeriodStart * 1000 - : now; - const periodEnd = subscription.currentPeriodEnd - ? subscription.currentPeriodEnd * 1000 - : now + 30 * 24 * 60 * 60 * 1000; + const periodStart = subscription.currentPeriodStart + ? subscription.currentPeriodStart * 1000 + : (console.warn(`Missing currentPeriodStart for subscription ${subscription.id}`), now); + const periodEnd = subscription.currentPeriodEnd + ? subscription.currentPeriodEnd * 1000 + : (console.warn(`Missing currentPeriodEnd for subscription ${subscription.id}`), now + 30 * 24 * 60 * 60 * 1000);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
package.jsonsrc/agents/agents/code-generation.tssrc/agents/agents/validation.tssrc/agents/sandbox.tssrc/app/(home)/pricing/page-content.tsxsrc/app/(home)/subscription/page.tsxsrc/app/api/generate/route.tssrc/app/api/stripe/checkout/route.tssrc/app/api/stripe/portal/route.tssrc/app/api/webhooks/clerk/route.tssrc/app/api/webhooks/polar/route.tssrc/app/api/webhooks/stripe/route.tssrc/components/clerk-provider-wrapper.tsxsrc/lib/stripe/index.tssrc/modules/projects/ui/components/message-card.tsxsrc/modules/projects/ui/components/message-form.tsxsrc/modules/projects/ui/components/messages-container.tsx
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/app/api/webhooks/stripe/route.tssrc/app/api/webhooks/clerk/route.tssrc/app/api/stripe/checkout/route.tssrc/agents/agents/code-generation.tssrc/modules/projects/ui/components/message-card.tsxsrc/app/api/webhooks/polar/route.tssrc/components/clerk-provider-wrapper.tsxsrc/app/api/generate/route.tssrc/modules/projects/ui/components/message-form.tsxsrc/modules/projects/ui/components/messages-container.tsxsrc/lib/stripe/index.tssrc/agents/sandbox.tssrc/app/api/stripe/portal/route.tssrc/app/(home)/subscription/page.tsxsrc/app/(home)/pricing/page-content.tsxsrc/agents/agents/validation.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification
Files:
src/app/api/webhooks/stripe/route.tssrc/app/api/webhooks/clerk/route.tssrc/app/api/stripe/checkout/route.tssrc/app/api/webhooks/polar/route.tssrc/app/api/generate/route.tssrc/app/api/stripe/portal/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/app/api/webhooks/stripe/route.tssrc/app/api/webhooks/clerk/route.tssrc/app/api/stripe/checkout/route.tssrc/agents/agents/code-generation.tssrc/modules/projects/ui/components/message-card.tsxsrc/app/api/webhooks/polar/route.tssrc/components/clerk-provider-wrapper.tsxsrc/app/api/generate/route.tssrc/modules/projects/ui/components/message-form.tsxsrc/modules/projects/ui/components/messages-container.tsxsrc/lib/stripe/index.tssrc/agents/sandbox.tssrc/app/api/stripe/portal/route.tssrc/app/(home)/subscription/page.tsxsrc/app/(home)/pricing/page-content.tsxsrc/agents/agents/validation.ts
src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Convex real-time database subscriptions to enable UI updates when data changes in
projects,messages,fragments,usage,oauthConnections, andimportstablesImplement accessibility and responsive design in UI components
Files:
src/components/clerk-provider-wrapper.tsx
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Always use
bunfor package management (bun install, bun add, bun remove). Never use npm or yarn.
Files:
package.json
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/auth.config.ts : Keep OAuth tokens encrypted in Convex; never expose API keys in client-side code (use NEXT_PUBLIC_ prefix only for public values)
Applied to files:
src/app/api/webhooks/stripe/route.tssrc/app/api/stripe/portal/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/app/api/**/*.ts : Sync credit usage with Clerk custom claim `plan: 'pro'` for Pro tier verification
Applied to files:
src/app/api/webhooks/stripe/route.tssrc/app/api/webhooks/clerk/route.tssrc/app/api/stripe/checkout/route.tssrc/app/api/generate/route.tssrc/lib/stripe/index.tssrc/app/api/stripe/portal/route.tssrc/app/(home)/pricing/page-content.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/middleware.ts : Use Clerk authentication middleware in `src/middleware.ts` for end-to-end type safety
Applied to files:
src/app/api/webhooks/clerk/route.tssrc/app/api/stripe/checkout/route.tssrc/components/clerk-provider-wrapper.tsxsrc/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/agents/agents/code-generation.tssrc/app/api/generate/route.tssrc/modules/projects/ui/components/message-form.tsxsrc/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/prompts/*.ts : Framework-specific AI prompts must be maintained in `src/prompts/` with separate files per framework (nextjs.ts, angular.ts, etc.)
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/agents/agents/code-generation.tssrc/app/api/generate/route.tssrc/agents/sandbox.tssrc/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/agents/code-generation.tssrc/agents/sandbox.tssrc/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Be strict with types, particularly around IDs of documents; use `Id<'tableName'>` rather than `string` for function parameters
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/**/*.{ts,tsx} : Use modern framework patterns: Next.js App Router and React hooks
Applied to files:
src/app/api/generate/route.tssrc/modules/projects/ui/components/messages-container.tsxsrc/app/(home)/subscription/page.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `query`, `mutation`, and `action` to register public functions that are exposed to the public Internet
Applied to files:
src/app/api/generate/route.tssrc/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/schema.ts : Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in `usage` table with rolling 24-hour expiration
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always use the `v.null()` validator when returning a null value from a Convex function
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use Convex real-time database subscriptions to enable UI updates when data changes in `projects`, `messages`, `fragments`, `usage`, `oauthConnections`, and `imports` tables
Applied to files:
src/modules/projects/ui/components/message-form.tsxsrc/modules/projects/ui/components/messages-container.tsxsrc/app/(home)/subscription/page.tsxsrc/app/(home)/pricing/page-content.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
Applied to files:
src/modules/projects/ui/components/message-form.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `api` object from `convex/_generated/api.ts` to call public functions registered with `query`, `mutation`, or `action`
Applied to files:
src/modules/projects/ui/components/message-form.tsxsrc/modules/projects/ui/components/messages-container.tsxsrc/app/(home)/subscription/page.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use the `internal` object from `convex/_generated/api.ts` to call internal functions registered with `internalQuery`, `internalMutation`, or `internalAction`
Applied to files:
src/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/trpc/query-client.ts : Use React Query for frontend caching, refetching, and optimistic updates via `src/trpc/query-client.ts`
Applied to files:
src/modules/projects/ui/components/messages-container.tsxsrc/app/(home)/subscription/page.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `ctx.runQuery` to call a query from a query, mutation, or action; specify type annotations on return values when calling functions in the same file
Applied to files:
src/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Use `internalQuery`, `internalMutation`, and `internalAction` to register private functions that are not part of the public API
Applied to files:
src/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/**/*.ts : Use Convex database (`src/api/convex/*`) for all new data operations; never use deprecated PostgreSQL schema (`prisma/`)
Applied to files:
src/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : ALWAYS include argument and return validators for all Convex functions, including `query`, `internalQuery`, `mutation`, `internalMutation`, `action`, and `internalAction`
Applied to files:
src/modules/projects/ui/components/messages-container.tsx
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to next.config.ts : Configure Next.js with Turbopack in dev mode, image optimization (AVIF, WebP), CSS optimization with Critters, and strict mode enabled in `next.config.ts`
Applied to files:
package.json
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always add `types/node` to your `package.json` when using any Node.js built-in modules
Applied to files:
package.json
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Never start dev servers in E2B sandboxes - only run build and lint validation
Applied to files:
src/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to package.json : Always use `bun` for package management (bun install, bun add, bun remove). Never use npm or yarn.
Applied to files:
src/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Always use `bun` for installing packages and running scripts, not npm or pnpm
Applied to files:
src/agents/agents/validation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/functions.ts : Update E2B template name in `src/inngest/functions.ts` (line ~22) after building new templates
Applied to files:
src/agents/agents/validation.ts
🧬 Code graph analysis (12)
src/app/api/webhooks/stripe/route.ts (2)
src/lib/stripe/index.ts (3)
Stripe(147-147)getStripe(5-15)stripe(17-22)tests/mocks/convex-browser.ts (1)
ConvexHttpClient(1-11)
src/app/api/stripe/checkout/route.ts (5)
src/lib/stripe/index.ts (3)
Stripe(147-147)getStripe(5-15)stripe(17-22)tests/mocks/convex-browser.ts (1)
ConvexHttpClient(1-11)src/app/api/stripe/portal/route.ts (1)
POST(27-63)src/app/api/webhooks/stripe/route.ts (1)
POST(54-217)test-openrouter.js (1)
baseUrl(18-18)
src/agents/agents/code-generation.ts (7)
src/agents/types.ts (3)
GenerationRequest(27-33)StreamUpdate(46-53)Framework(3-3)src/agents/logger.ts (2)
createLogger(78-80)error(47-60)src/agents/sandbox.ts (1)
sandboxManager(162-162)src/agents/tools.ts (1)
createTools(6-110)src/agents/client.ts (2)
getModel(68-71)ModelId(66-66)src/agents/prompts/index.ts (1)
getFrameworkPrompt(29-31)src/agents/retry.ts (2)
withRetry(23-64)retryOnTransient(78-85)
src/modules/projects/ui/components/message-card.tsx (1)
src/lib/utils.ts (1)
cn(6-8)
src/app/api/webhooks/polar/route.ts (1)
src/lib/polar-client.ts (1)
getPolarWebhookSecret(52-61)
src/app/api/generate/route.ts (6)
src/agents/types.ts (2)
StreamUpdate(46-53)Framework(3-3)convex/_generated/dataModel.d.ts (1)
Id(48-49)src/agents/sandbox.ts (1)
sandboxManager(162-162)src/agents/agents/code-generation.ts (1)
generateCode(28-163)src/agents/agents/validation.ts (1)
runValidation(5-35)src/agents/agents/error-fixer.ts (1)
fixErrors(22-70)
src/modules/projects/ui/components/message-form.tsx (2)
src/agents/client.ts (1)
ModelId(66-66)src/agents/index.ts (1)
ModelId(2-2)
src/modules/projects/ui/components/messages-container.tsx (2)
src/modules/projects/ui/components/message-loading.tsx (1)
MessageLoading(36-54)src/modules/projects/ui/components/message-form.tsx (1)
MessageForm(47-485)
src/agents/sandbox.ts (2)
tests/mocks/e2b-code-interpreter.ts (1)
Sandbox(1-37)src/agents/types.ts (2)
Framework(3-3)TerminalResult(60-64)
src/app/api/stripe/portal/route.ts (1)
src/lib/stripe/index.ts (3)
Stripe(147-147)getStripe(5-15)stripe(17-22)
src/app/(home)/pricing/page-content.tsx (1)
src/lib/stripe/index.ts (1)
PLANS(24-50)
src/agents/agents/validation.ts (4)
src/agents/index.ts (3)
runValidation(16-16)createLogger(8-8)sandboxManager(6-6)src/agents/types.ts (1)
ValidationResult(40-44)src/agents/logger.ts (1)
createLogger(78-80)src/agents/sandbox.ts (1)
sandboxManager(162-162)
🪛 GitHub Check: CodeQL
src/agents/sandbox.ts
[failure] 63-63: Use of externally-controlled format string
Format string depends on a user-provided value.
🔇 Additional comments (28)
src/app/api/webhooks/polar/route.ts (3)
11-12: LGTM! Consistent error handling with appropriate status codes.The error responses are well-structured with appropriate HTTP status codes (401 for authentication failures, 500 for server/configuration errors) and consistent JSON format.
Also applies to: 19-20, 28-29, 56-57
34-34: LGTM! Simplified logging is clear and concise.The streamlined logging format still captures the essential information (event type) while reducing verbosity. This aligns well with the overall cleanup objectives of the PR.
Also applies to: 51-51
46-48: Clarify intent of order.created logging-only behavior.The
order.createdevent currently only logs without any processing. While this is consistent with the current Polar webhook pattern (subscription events similarly note "handled by Stripe webhooks"), it's unclear whether this is intentional.If Polar orders are meant to be informational only with processing handled elsewhere, please add a comment explaining this design. If orders require processing (inventory updates, database records, fulfillment workflows, etc.), implement the necessary logic.
src/app/api/webhooks/clerk/route.ts (3)
14-16: LGTM: Error handling is clear and appropriate.The simplified error messages are concise and appropriate for webhook responses. The addition of
console.errorfor verification failures (line 40) improves observability without leaking sensitive information.Also applies to: 23-25, 39-42
1-4: LGTM: Webhook verification follows best practices.The imports and webhook verification logic (using Svix) follow Clerk's recommended pattern and security best practices.
Also applies to: 11-38
44-68: Align Pro tier verification with coding guideline requirement.The Clerk webhook appropriately delegates subscription handling to the Stripe webhook, which syncs subscription data to Convex. However, this creates an architectural gap: the coding guidelines require "Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification," but the current implementation determines Pro tier eligibility from Stripe subscription data stored in Convex, not from Clerk custom claims.Consider updating the Stripe webhook to set the Clerk user's
plancustom claim to'pro'when subscription status changes toactive, and sync it back to'free'on cancellation. This aligns the Pro tier verification with the specified architectural guideline.src/agents/sandbox.ts (2)
107-122: LGTM on cache cleanup implementation.The scheduled cleanup now properly calls
sandbox.kill()before removing the cache entry, and error handling is in place. This addresses the concern about keeping sandboxes alive unnecessarily.
63-63: Remove this comment — no format string vulnerability exists in JavaScript template literals.Template literals in JavaScript (
${variable}) perform string concatenation, not format string interpretation. Unlike C'sprintf()function, JavaScript does not evaluate format specifiers like%xor%nwithin strings. There is no exploitable format string vulnerability here. The code is secure as written.Likely an incorrect or invalid review comment.
src/modules/projects/ui/components/message-card.tsx (2)
94-97: LGTM! Clean streaming state derivation.The streaming logic is well-implemented:
displayContentcorrectly prioritizesstreamingContentovercontentisStreamingdefensively checks both the message type AND presence of streaming content, preventing false positives
115-130: Good streaming UX indicators.The visual feedback for streaming state is appropriate:
- Header indicator with pulsing dot and "Thinking..." text
- Inline animated cursor that follows the content
Minor consideration: the inline cursor (Line 129) uses
bg-primary/60which may have low contrast on some themes. This is non-blocking.src/modules/projects/ui/components/messages-container.tsx (2)
28-43: LGTM! Well-structured streaming state management.The streaming callbacks are properly memoized with
useCallbackand have correct dependencies:
handleStreamUpdateappends content incrementallyhandleStreamStartresets state cleanlyhandleStreamEndclears both streaming content and flag
78-80: Consider enforcing single streaming message per project or usefindLast()for consistency.The code assumes only one STREAMING message exists per project, but there's no enforcement preventing concurrent requests from creating multiple STREAMING messages. Using
find()returns the first match, not the most recent—the variable name "lastStreamingMessage" is misleading. To be safe, either:
- Add a database mutation that clears previous STREAMING messages before creating a new one, or
- Use
findLast()to reliably get the most recent streaming messagesrc/modules/projects/ui/components/message-form.tsx (4)
13-13: LGTM! Import updated for new agent architecture.The
ModelIdtype import correctly moved from the removed Inngest module to the new agents client.
88-101: Good error handling for the fetch response.This addresses the previous review comment about missing response handling. The error extraction and status text fallback are well implemented.
103-152: SSE parsing implementation is well-structured.The streaming implementation correctly:
- Uses
TextDecoderwithstream: truefor proper chunk handling- Buffers partial lines across chunks
- Parses
data:prefixed SSE events- Handles different event types (error, stream, status, complete)
- Catches JSON parse errors without crashing the loop
126-130:onStreamEndcalled before throwing may cause double invocation.When an error event is received,
onStreamEndis called on line 128, then the error is thrown and caught in the outer catch block. However,onStreamEndis also called on line 154 after the loop. If an error occurs, this could lead toonStreamEndbeing called twice (once in the error handler, once after the loop completes due to the throw).🔎 Proposed fix
if (data.type === "error") { toast.error(data.error || "Generation failed"); - onStreamEnd?.(); throw new Error(data.error || "Generation failed"); }The
onStreamEndcall on line 154 will handle cleanup. Alternatively, use a flag to track if streaming ended:+ let streamEnded = false; let buffer = ""; while (true) { // ... existing code ... if (data.type === "complete") { console.log("Generation complete:", data.message); + streamEnded = true; onStreamEnd?.(); } // ... } - onStreamEnd?.(); + if (!streamEnded) { + onStreamEnd?.(); + }Likely an incorrect or invalid review comment.
package.json (3)
71-71: critters dependency is necessary for CSS optimization.The dependency is explicitly configured in
next.config.ts.bakandnext.config.mjswhere it's checked viarequire.resolve("critters")to enable theexperimental.optimizeCssfeature. Previous build logs confirm this dependency was missing, which disabled CSS optimization. Adding critters to package.json correctly resolves this configuration.
21-21: Verify compatibility with major version upgrades—these packages have confirmed breaking changes.Three packages have major version bumps with significant breaking changes:
@e2b/code-interpreter(1.x → 2.x): Sandbox instantiation changed from constructor to.create()method;timeoutparameter renamed totimeoutMs; file and list APIs updated; default to secure sandboxes.@hookform/resolvers(3.x → 5.x): Type inference refactored (Input/Output separation); requiresreact-hook-form >= 7.55.0.@types/node(24.x → 25.x):ErrorEventnow global;SlowBufferand other long-deprecated APIs removed.Confirm that existing code has been updated to use new method signatures, parameter names, and type structures.
15-16: No issues found. Both@ai-sdk-tools/agentsand@ai-sdk-tools/memoryat version 1.2.0 exist in the npm registry and are properly specified.src/app/api/stripe/portal/route.ts (1)
27-63: LGTM!The POST handler correctly authenticates via Clerk, fetches the customer from Convex, validates the Stripe customer ID exists, and creates a billing portal session with proper error handling.
src/app/(home)/pricing/page-content.tsx (2)
47-53: Verify Pro status is synced with Clerk custom claim.Based on learnings, Pro tier verification should sync with Clerk custom claim
plan: 'pro'. The current implementation relies solely on Convex subscription data. Consider also checking the Clerk session claim to ensure consistency, or confirm that the webhook properly updates Clerk claims when subscription status changes.
64-114: LGTM!The
handleCheckoutandhandleManageSubscriptionhandlers properly manage loading states, handle errors with user-friendly toast notifications, and redirect to the appropriate URLs on success.src/app/api/stripe/checkout/route.ts (1)
108-128: LGTM!The checkout session creation correctly stores
userIdin subscription metadata, which aligns with the webhook handler's expectations for linking subscriptions to users.src/app/api/webhooks/stripe/route.ts (1)
54-86: LGTM!The webhook signature verification properly validates the presence of headers and secret, then constructs and verifies the event with appropriate error responses.
src/lib/stripe/index.ts (4)
5-15: LGTM!The
getStripe()factory properly validates the environment variable and lazily initializes the Stripe client with the correct API version.
54-81: LGTM!The
getOrCreateCustomerfunction properly handles the customer lookup by email, updates metadata if needed, and creates new customers with proper metadata linking.
109-145: LGTM!The portal session, subscription management, and cancellation helpers are well-encapsulated and follow Stripe best practices.
9-12: The Stripe API version2025-12-15.cloveris valid and is the current GA (General Availability) version. No action required.
src/agents/agents/code-generation.ts
Outdated
| const project = await getConvex().query(api.projects.getForSystem, { | ||
| projectId: request.projectId as Id<'projects'>, | ||
| }); |
There was a problem hiding this comment.
Validate projectId before casting to Id<'projects'>.
The projectId from the request is cast to Id<'projects'> without validation. Per coding guidelines, be strict with types, particularly around IDs of documents. If the projectId is invalid, the Convex query will fail with a less clear error.
🔎 Proposed fix
+ // Validate projectId format before casting
+ if (!request.projectId || typeof request.projectId !== 'string') {
+ throw new Error('Invalid projectId');
+ }
+
const project = await getConvex().query(api.projects.getForSystem, {
projectId: request.projectId as Id<'projects'>,
});As per coding guidelines, be strict with types, particularly around IDs of documents.
🤖 Prompt for AI Agents
In src/agents/agents/code-generation.ts around lines 51 to 53, the
request.projectId is being cast to Id<'projects'> without validation; validate
that request.projectId is present and is a string (and optionally matches your
ID format/length or use the project's isId helper) before casting, and throw a
clear, typed error (or return a 400-style validation error) if invalid; then
pass the validated/cast id into getConvex().query so the Convex call never
receives an unchecked cast and the failure message is actionable.
| if (customer?.stripeCustomerId) { | ||
| stripeCustomerId = customer.stripeCustomerId; | ||
| } else { | ||
| const existingCustomers = await stripe.customers.list({ | ||
| email, | ||
| limit: 1, | ||
| }); | ||
|
|
||
| if (existingCustomers.data.length > 0) { | ||
| stripeCustomerId = existingCustomers.data[0].id; | ||
| await stripe.customers.update(stripeCustomerId, { | ||
| metadata: { userId }, | ||
| }); | ||
| } else { | ||
| const newCustomer = await stripe.customers.create({ | ||
| email, | ||
| name: name || undefined, | ||
| metadata: { userId }, | ||
| }); | ||
| stripeCustomerId = newCustomer.id; | ||
| } | ||
|
|
||
| await convex.mutation(api.subscriptions.createOrUpdateCustomer, { | ||
| userId, | ||
| stripeCustomerId, | ||
| email, | ||
| name: name || undefined, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Potential race condition during customer creation.
If two concurrent requests arrive for a new user, both could pass the customer?.stripeCustomerId check and potentially create duplicate Stripe customers or race on the Convex mutation. Consider using a locking mechanism or idempotency key for the Stripe customer creation.
🔎 Proposed mitigation
} else {
const newCustomer = await stripe.customers.create({
email,
name: name || undefined,
metadata: { userId },
+ }, {
+ idempotencyKey: `customer-create-${userId}`,
});
stripeCustomerId = newCustomer.id;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (customer?.stripeCustomerId) { | |
| stripeCustomerId = customer.stripeCustomerId; | |
| } else { | |
| const existingCustomers = await stripe.customers.list({ | |
| email, | |
| limit: 1, | |
| }); | |
| if (existingCustomers.data.length > 0) { | |
| stripeCustomerId = existingCustomers.data[0].id; | |
| await stripe.customers.update(stripeCustomerId, { | |
| metadata: { userId }, | |
| }); | |
| } else { | |
| const newCustomer = await stripe.customers.create({ | |
| email, | |
| name: name || undefined, | |
| metadata: { userId }, | |
| }); | |
| stripeCustomerId = newCustomer.id; | |
| } | |
| await convex.mutation(api.subscriptions.createOrUpdateCustomer, { | |
| userId, | |
| stripeCustomerId, | |
| email, | |
| name: name || undefined, | |
| }); | |
| } | |
| if (customer?.stripeCustomerId) { | |
| stripeCustomerId = customer.stripeCustomerId; | |
| } else { | |
| const existingCustomers = await stripe.customers.list({ | |
| email, | |
| limit: 1, | |
| }); | |
| if (existingCustomers.data.length > 0) { | |
| stripeCustomerId = existingCustomers.data[0].id; | |
| await stripe.customers.update(stripeCustomerId, { | |
| metadata: { userId }, | |
| }); | |
| } else { | |
| const newCustomer = await stripe.customers.create({ | |
| email, | |
| name: name || undefined, | |
| metadata: { userId }, | |
| }, { | |
| idempotencyKey: `customer-create-${userId}`, | |
| }); | |
| stripeCustomerId = newCustomer.id; | |
| } | |
| await convex.mutation(api.subscriptions.createOrUpdateCustomer, { | |
| userId, | |
| stripeCustomerId, | |
| email, | |
| name: name || undefined, | |
| }); | |
| } |
🤖 Prompt for AI Agents
In src/app/api/stripe/checkout/route.ts around lines 58 to 86, the current flow
can create duplicate Stripe customers or race on the Convex mutation when two
requests run concurrently; to fix it, make customer creation idempotent and
atomic by first trying to persist an upsert/transaction in Convex that reserves
or returns a stripeCustomerId (or a creation-in-progress flag) and only create a
Stripe customer if Convex indicates none exists, and when calling
stripe.customers.create include a unique idempotency key (e.g., derived from
userId + request fingerprint) so concurrent creates de-duplicate; after
successful create, write the stripeCustomerId back in a single Convex mutation
(or within the same transaction) and on failure re-check Convex for a stored
stripeCustomerId before retrying.
| type ClerkEvent = | ||
| | WebhookEvent | ||
| | { type: "subscription.created"; data: any } | ||
| | { type: "subscription.updated"; data: any } | ||
| | { type: "subscription.deleted"; data: any }; | ||
| | { type: "subscription.created"; data: unknown } | ||
| | { type: "subscription.updated"; data: unknown } | ||
| | { type: "subscription.deleted"; data: unknown }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, read the Clerk webhook file to examine the type definition and line 61
cat -n src/app/api/webhooks/clerk/route.ts | head -70Repository: Jackson57279/zapdev
Length of output: 2516
🏁 Script executed:
# Search for Stripe webhook handler or subscription event handling
find . -path "*/node_modules" -prune -o -type f -name "*.ts" -o -name "*.tsx" | grep -i "webhook\|stripe" | head -20Repository: Jackson57279/zapdev
Length of output: 279
🏁 Script executed:
# Check for subscription.created, subscription.updated, subscription.deleted handling
rg -n "subscription\.(created|updated|deleted)" --type=tsRepository: Jackson57279/zapdev
Length of output: 954
Remove unused subscription events from ClerkEvent type union.
The ClerkEvent type includes subscription.created, subscription.updated, and subscription.deleted events (lines 7-9), but these will never be received by this Clerk webhook endpoint. Clerk does not emit subscription events natively. The actual subscription events are handled by the Stripe webhook (src/app/api/webhooks/stripe/route.ts) which uses different event type names (customer.subscription.*).
The case statements for these events (lines 58-62) are dead code that will never execute. Remove the subscription event types from the ClerkEvent union to eliminate confusion and maintain accurate type definitions.
🤖 Prompt for AI Agents
In src/app/api/webhooks/clerk/route.ts around lines 5–9, the ClerkEvent union
includes subscription.created/updated/deleted which Clerk never emits; remove
those three union members from the type definition; also remove the dead
switch/case branch handling those events around lines ~58–62 (or refactor the
switch to no longer reference those case labels) so the code and types remain
consistent and the file compiles cleanly.
| case "subscription.created": | ||
| case "subscription.updated": | ||
| case "subscription.active": { | ||
| const subscription = event.data; | ||
| const userId = subscription.metadata?.userId as string | undefined; | ||
|
|
||
| if (!userId) { | ||
| console.error("❌ Missing userId in subscription metadata"); | ||
| return NextResponse.json( | ||
| { error: "Missing userId in metadata" }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| const now = Date.now(); | ||
| const periodStart = subscription.currentPeriodStart | ||
| ? new Date(subscription.currentPeriodStart).getTime() | ||
| : now; | ||
| const periodEnd = subscription.currentPeriodEnd | ||
| ? new Date(subscription.currentPeriodEnd).getTime() | ||
| : now + 30 * 24 * 60 * 60 * 1000; | ||
|
|
||
| await convex.mutation(api.subscriptions.createOrUpdateSubscription, { | ||
| userId, | ||
| clerkSubscriptionId: subscription.id, | ||
| planId: subscription.productId, | ||
| planName: extractPlanName(subscription.product?.name), | ||
| status: mapPolarStatus(subscription.status), | ||
| currentPeriodStart: periodStart, | ||
| currentPeriodEnd: periodEnd, | ||
| cancelAtPeriodEnd: subscription.cancelAtPeriodEnd ?? false, | ||
| metadata: { | ||
| ...subscription.metadata, | ||
| polarCustomerId: subscription.customerId, | ||
| source: "polar", | ||
| }, | ||
| }); | ||
|
|
||
| console.log(`✅ Subscription ${event.type} for user ${userId}`); | ||
| case "subscription.active": | ||
| case "subscription.canceled": | ||
| case "subscription.revoked": | ||
| case "subscription.uncanceled": | ||
| console.log(`Polar subscription event ${event.type} - handled by Stripe webhooks`); | ||
| break; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify subscription handling across webhook routes
echo "=== Checking Stripe webhook handler for subscription handling ==="
fd -t f -e ts "stripe" src/app/api/webhooks/ 2>/dev/null | head -10
echo -e "\n=== Files in webhook directory ==="
fd -t f -e ts . src/app/api/webhooks/ 2>/dev/null
echo -e "\n=== Checking Stripe webhook handler for Polar subscription handling ==="
if [ -f src/app/api/webhooks/stripe/route.ts ]; then
cat -n src/app/api/webhooks/stripe/route.ts
else
echo "Stripe webhook handler not found"
fi
echo -e "\n=== Checking for Convex mutations related to subscriptions ==="
rg -n "convex.*subscription|subscription.*mutation" src/ --type ts -C 2 | head -50
echo -e "\n=== Checking Polar webhook content ==="
cat -n src/app/api/webhooks/polar/route.ts | head -100Repository: Jackson57279/zapdev
Length of output: 12331
Critical: Polar subscription events are not being tracked.
These Polar subscription events are logged but never processed. The comment "handled by Stripe webhooks" is misleading — Polar and Stripe webhooks are separate; only Stripe's customer.subscription.* events are processed by the Stripe handler. Removing the Convex mutations here without replacement means Polar subscription creation, updates, and cancellations are never synced to the database, breaking subscription tracking and user access management.
Replace the logging-only stubs with actual subscription mutations (similar to the Stripe handler's implementation in lines 122-135), or confirm Polar subscriptions are handled through an alternative mechanism.
🤖 Prompt for AI Agents
In src/app/api/webhooks/polar/route.ts around lines 37 to 44, Polar subscription
events are only logged and not persisted; replace the console-only stubs with
actual Convex subscription mutations similar to the Stripe handler at lines
122-135 so Polar subscriptions are synced to the DB. For each Polar event type
(subscription.created, .updated, .active, .canceled, .revoked, .uncanceled) call
the corresponding Convex mutation (createOrUpdateSubscription for
create/updated/active, cancelSubscription for canceled/revoked,
restoreSubscription for uncanceled) using event.data.payload (map Polar
subscription ID to your internal subscription id and include customer/user id),
handle and await the mutation result, add error handling/logging around the
mutation, and keep the same idempotency checks you use in the Stripe handler to
avoid duplicate records.
| if (data.type === "complete") { | ||
| console.log("Generation complete:", data.message); | ||
| onStreamEnd?.(); | ||
| } |
There was a problem hiding this comment.
Potential double onStreamEnd call on "complete" event.
onStreamEnd is called both when the "complete" event is received (line 142) and unconditionally after the while loop (line 154). If the server sends a "complete" event before the stream ends, onStreamEnd will be invoked twice.
🔎 Proposed fix: Use early return or flag
if (data.type === "complete") {
console.log("Generation complete:", data.message);
onStreamEnd?.();
+ return; // Exit early after completion
}Or remove the onStreamEnd call on line 142 and rely solely on line 154.
🤖 Prompt for AI Agents
In src/modules/projects/ui/components/message-form.tsx around lines 140-143 (and
the subsequent unconditional call at ~154), the onStreamEnd callback can be
invoked twice when a "complete" event is emitted before the stream loop exits;
add a guard boolean (e.g., let streamEnded = false) in the enclosing scope and
before calling onStreamEnd check if streamEnded is false, set streamEnded = true
and then call onStreamEnd, and remove any other direct calls that don't respect
this guard (or alternatively remove the onStreamEnd call at line 142 and rely on
the single end call at line 154) so onStreamEnd is only invoked once.
|
🔍 Analyzing PR changes and preparing to run tests... |
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/agents/agents/code-generation.ts (2)
160-162: Validate projectId before casting to Id<'projects'>.The
projectIdis cast without validation. Per coding guidelines, be strict with types, particularly around IDs of documents. If the projectId is invalid or malformed, the Convex query will fail with a less actionable error message.🔎 Add validation
+ if (!request.projectId || typeof request.projectId !== 'string') { + throw new Error('Invalid projectId: must be a non-empty string'); + } + const project = await getConvex().query(api.projects.getForSystem, { projectId: request.projectId as Id<'projects'>, });As per coding guidelines, be strict with types, particularly around IDs of documents.
169-171: Await onProgress in the file write callback.The callback passed to
createAgentToolscallsonProgress, which returnsPromise<void>, but the promise is not awaited. This can cause unhandled promise rejections or race conditions if subsequent progress updates occur before this one completes.🔎 Make callback async and await onProgress
- const tools = createAgentTools(sandbox, files, (path) => { - onProgress({ type: 'file', filePath: path }); + const tools = createAgentTools(sandbox, files, async (path) => { + await onProgress({ type: 'file', filePath: path }); });Additionally, update the
onFileWriteparameter type increateAgentTools(line 33) to acceptPromise<void>:onFileWrite?: (path: string) => void + onFileWrite?: (path: string) => void | Promise<void>And await it in the tool execution (line 56):
- onFileWrite?.(file.path); + await onFileWrite?.(file.path);
🧹 Nitpick comments (2)
src/agents/agents/code-generation.ts (2)
107-107: Redundant nullish coalescing operator.The
timeoutMsparameter already defaults to60000on line 95, so the?? 60000on this line is redundant.🔎 Simplify
- const result = await sandbox.commands.run(command, { timeoutMs: timeoutMs ?? 60000 }); + const result = await sandbox.commands.run(command, { timeoutMs });
206-238: Consider using logger instead of console.log for consistency.The function uses
console.logfor debugging output (lines 206, 210, 217, 225, 230, 237-238), while a structuredloggeris already initialized and used elsewhere. Using the logger consistently would improve observability and allow these logs to be captured by Sentry or other monitoring tools.🔎 Example replacements
- console.log('[AI] Tool call:', call.toolName); + logger.progress('tool-call', `Tool call: ${call.toolName}`);- console.log('\n[AI] Stream complete'); + logger.progress('stream', 'Stream complete');- console.log('[AI] Total steps:', steps.length); + logger.progress('steps', `Total steps: ${steps.length}`);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/agents/agents/code-generation.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/agents/agents/code-generation.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/agents/agents/code-generation.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/prompts/*.ts : Framework-specific AI prompts must be maintained in `src/prompts/` with separate files per framework (nextjs.ts, angular.ts, etc.)
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Be strict with types, particularly around IDs of documents; use `Id<'tableName'>` rather than `string` for function parameters
Applied to files:
src/agents/agents/code-generation.ts
🔇 Additional comments (1)
src/agents/agents/code-generation.ts (1)
178-178: getModel already handles invalid model IDs gracefully with a fallback to 'auto'.The implementation in
src/agents/client.ts(lines 68-71) includes a fallback mechanism:const config = MODEL_CONFIGS[modelId] || MODEL_CONFIGS['auto'];. This ensures that invalid model IDs are handled without throwing errors. The try-catch block at line 178 provides an additional safety layer for unexpected errors fromgetOpenRouter(). No changes needed.
| const escapedPath = path.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); | ||
| const result = await sandbox.commands.run( | ||
| `find -- "${escapedPath}" \\( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.css" \\) -type f -print0` | ||
| ); |
There was a problem hiding this comment.
Shell injection risk in path escaping.
The escaping on line 123 only handles backslashes and double quotes, but shell metacharacters like $, backticks, $(...), and others can still enable command injection if an attacker controls the directory path. The find command is executed without proper quoting or sanitization.
🔎 Safer alternatives
Option 1: Use shell-quote or a similar library to escape the path safely:
+import { quote } from 'shell-quote';
+
execute: async ({ path }) => {
- const escapedPath = path.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
+ const escapedPath = quote([path]);
const result = await sandbox.commands.run(
- `find -- "${escapedPath}" \\( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.css" \\) -type f -print0`
+ `find -- ${escapedPath} \\( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.css" \\) -type f -print0`
);Option 2: If the sandbox API supports passing arguments as an array, use that instead of string interpolation.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/agents/agents/code-generation.ts around lines 123 to 126, the current
manual escaping of backslashes and double quotes is insufficient and leaves the
find command vulnerable to shell injection via other metacharacters; replace the
string-interpolated shell invocation with a safe approach: either call
sandbox.commands.run with an arguments array (no interpolation) so the path is
passed as a single arg, or run the path through a well-tested shell escaping
library (e.g., shell-quote) before insertion; additionally validate the path
(ensure it’s within allowed base dirs and contains no null bytes) and prefer the
argument-array approach when supported by the sandbox API.
|
🔍 Analyzing PR changes and preparing to run tests... |
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/prompts/shared.ts (1)
180-190: Duplicate numbered item in the list.There are two items numbered "3." — one at line 180 (Handle All Errors) and another at line 186 (Type Safety). This appears to be a typo.
🔎 Proposed fix
3. Handle All Errors: Every function must include proper error handling: - Use try-catch blocks for async operations and code that might fail - Validate all user inputs and external data - Return meaningful error messages - Never let errors crash the application silently -3. Type Safety: +4. Type Safety: - Use TypeScript properly with explicit types (no "any" unless absolutely necessary) - Define interfaces for all props and data structures - Ensure all function parameters and return types are typed - Fix all TypeScript errors before completing -4. Code Validation (MANDATORY): +5. Code Validation (MANDATORY):
♻️ Duplicate comments (8)
src/app/api/generate/route.ts (4)
115-118: Consider explicit project validation.While optional chaining provides a fallback when
projectis null, an explicit null check after the query would provide clearer error messaging if the project doesn't exist. The current code silently falls back to 'NEXTJS' framework.🔎 Proposed improvement
const project = await convex.query(api.projects.getForSystem, { projectId: projectId as Id<'projects'>, }); + + if (!project) { + await sendUpdate({ type: 'error', error: 'Project not found' }); + await writer.close(); + return new Response('Project not found', { status: 404 }); + } + console.log('[GENERATE] Project framework:', project?.framework);
13-42: Missing Pro tier verification.Per coding guidelines, API routes should sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification. The current implementation only checks credits but doesn't verify the user's plan tier.Based on learnings, the credit system requires Free tier (5 generations/24h) and Pro tier (100 generations/24h) verification.
🔎 Proposed fix
export async function POST(request: NextRequest) { - const { userId } = await auth(); + const { userId, sessionClaims } = await auth(); if (!userId) { return new Response('Unauthorized', { status: 401 }); } + // Verify Pro tier access + const isPro = sessionClaims?.plan === 'pro'; + const convex = await getConvexClientWithAuth(); try { const creditResult = await convex.mutation(api.usage.checkAndConsumeCreditForUser, { userId, }); if (!creditResult.success) { return new Response( JSON.stringify({ error: 'Insufficient credits', message: creditResult.message, - remaining: creditResult.remaining + remaining: creditResult.remaining, + tier: isPro ? 'pro' : 'free' }), { status: 402, headers: { 'Content-Type': 'application/json' } } ); } } catch (error) { console.error('Failed to check credits:', error); return new Response('Failed to verify credits', { status: 500 }); }
158-171: Validation failures are not handled after auto-fix attempts.After running validation and error fixes, the code doesn't check the final
validation.successstate. If validation still fails after fix attempts, the message is marked asCOMPLETEand a fragment is saved, potentially storing broken code.🔎 Proposed fix
if (hasFiles) { await sendUpdate({ type: 'status', message: 'Validating code...' }); let validation = await runValidation(effectiveSandboxId); if (!validation.success) { console.log('[GENERATE] Validation failed, attempting fixes'); await sendUpdate({ type: 'status', message: 'Fixing errors...' }); validation = await fixErrors(effectiveSandboxId, validation.errors || [], 0, sendUpdate); + + // Check final validation state + if (!validation.success) { + console.error('[GENERATE] Validation failed after fixes'); + await sendUpdate({ + type: 'error', + error: 'Code validation failed after fix attempts. Manual review required.' + }); + await writer.close(); + return; + } } else { console.log('[GENERATE] Validation passed'); } } else { console.log('[GENERATE] No files generated, skipping validation'); }
173-197: Fragment persistence occurs regardless of validation state.This segment persists the generated code and marks the message complete without checking if validation succeeded (issue flagged in previous segment). Consider this in conjunction with the validation handling fix.
src/agents/agents/code-generation.ts (4)
184-191: Add validation for request.model before casting to ModelId.The
request.modelis typed asstringinGenerationRequestbut is cast toModelIdwithout validation. WhilegetModelhas a defensive fallback, explicitly validating the model ID against allowed values would provide clearer error messages and prevent silent fallbacks.🔎 Proposed improvement
+ // Validate model ID + const validModels = ['auto', 'anthropic/claude-haiku-4.5', 'openai/gpt-5.1-codex', 'google/gemini-3-pro-preview', 'z-ai/glm-4.7', 'google/gemini-2.5-flash-lite', 'openai/gpt-4o'] as const; + + if (!validModels.includes(request.model as any)) { + logger.warn(`Invalid model ID: ${request.model}, falling back to 'auto'`); + } + let model; try { model = getModel(request.model as ModelId); } catch (error) { const errorMessage = `Failed to initialize AI model: ${error instanceof Error ? error.message : 'Unknown error'}`; logger.error(errorMessage, { error }); throw new Error(errorMessage); }
30-61: onFileWrite callback should support async/await.The
onFileWritecallback at line 56 is not awaited, but it's used to invokeonProgress(line 178) which returnsPromise<void>. This can cause unhandled promise rejections or race conditions.🔎 Proposed fix
function createAgentTools( sandbox: Sandbox, files: Record<string, string>, - onFileWrite?: (path: string) => void + onFileWrite?: (path: string) => void | Promise<void> ) { return { createOrUpdateFiles: tool({ description: 'Create or update files in the sandbox. Use this to write code files.', inputSchema: z.object({ files: z.array( z.object({ path: z.string().describe('File path relative to project root'), content: z.string().describe('File content'), }) ), }), execute: async ({ files: filesToWrite }) => { Sentry.addBreadcrumb({ category: 'tool', message: `Writing ${filesToWrite.length} files`, data: { paths: filesToWrite.map((f) => f.path) }, }); for (const file of filesToWrite) { await sandbox.files.write(file.path, file.content); files[file.path] = file.content; - onFileWrite?.(file.path); + await onFileWrite?.(file.path); } return { success: true, filesWritten: filesToWrite.map((f) => f.path) }; }, }),
117-134: Shell injection vulnerability in path escaping.The manual escaping only handles backslashes and double quotes, leaving the command vulnerable to injection via shell metacharacters like
$, backticks,$(...), and others. This is a security risk if path values are user-controlled.🔎 Recommended fix using shell-quote library
+import { quote } from 'shell-quote'; + listFiles: tool({ description: 'List files in a directory.', inputSchema: z.object({ path: z.string().describe('Directory path'), }), execute: async ({ path }) => { + // Validate path doesn't contain null bytes or escape base directory + if (path.includes('\0') || path.includes('..')) { + return { files: [], error: 'Invalid path' }; + } + - const escapedPath = path.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + const escapedPath = quote([path]); const result = await sandbox.commands.run( - `find -- "${escapedPath}" \\( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.css" \\) -type f -print0` + `find -- ${escapedPath} \\( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.css" \\) -type f -print0` ); const output = result.stdout || ''; const fileList = output.split('\0').filter(Boolean).slice(0, 50); return { files: fileList }; }, }),As per coding guidelines, sanitize file paths to prevent directory traversal and injection attacks.
160-174: Validate projectId before casting to Id<'projects'>.The
request.projectIdis cast toId<'projects'>without validation. Per coding guidelines, be strict with types around document IDs. If the projectId format is invalid, the Convex query will fail with an unclear error.🔎 Proposed fix
let framework: Framework = 'nextjs'; // Skip Convex query for test project IDs if (request.projectId.startsWith('test-')) { framework = 'nextjs'; logger.info('Using default framework for test project'); } else { + // Validate projectId format + if (!request.projectId || typeof request.projectId !== 'string' || request.projectId.trim() === '') { + throw new Error('Invalid projectId: must be a non-empty string'); + } + const project = await getConvex().query(api.projects.getForSystem, { projectId: request.projectId as Id<'projects'>, }); framework = (project?.framework?.toLowerCase() || 'nextjs') as Framework; }As per coding guidelines, be strict with types, particularly around IDs of documents.
🧹 Nitpick comments (4)
test-code-gen.ts (2)
1-2: Redundant dotenv imports.Line 1 imports
dotenv/configwhich auto-loads.env, then line 2 importsconfigand calls it again on line 8. Only one approach is needed.🔎 Proposed fix
-import 'dotenv/config'; -import { config } from 'dotenv'; +import { config } from 'dotenv'; import { generateCode } from './src/agents/agents/code-generation'; import type { StreamUpdate } from './src/agents/types'; import { sandboxManager } from './src/agents/sandbox';
25-25: Unused variableprogressUpdates.The
progressUpdatesarray is populated but never used after the test completes. Either remove it or use it in the results output.🔎 Proposed fix
- const progressUpdates: string[] = []; - const result = await generateCode( { projectId: 'test-project-id', @@ -34,7 +32,6 @@ async (update: StreamUpdate) => { if (update.type === 'status') { console.log(`[STATUS] ${update.message || ''}`); - if (update.message) progressUpdates.push(update.message); } else if (update.type === 'stream') {Or alternatively, log the collected updates:
console.log('═══════════════════════════════════════\n'); + console.log(`Progress updates collected: ${progressUpdates.length}`); console.log('✅ Test completed successfully!');plans/code-generation-tool-calling-fix.md (1)
1-285: Consider moving to explanations/ folder.Per coding guidelines, documentation files should be placed in the
explanations/folder. Consider moving this planning document there, or ifplans/is intended for temporary implementation plans, ensure it's cleaned up after the fixes are implemented.The content itself is well-structured with clear problem analysis, solution design, implementation checklist, testing plan, and rollback strategy.
plans/agent-architecture-improvements.md (1)
1-554: Documentation file location doesn't follow coding guidelines.Per coding guidelines, markdown files should be minimized and placed in the
@explanationsfolder when necessary. This architectural plan document is currently inplans/rather than@explanations/.As per coding guidelines, consider relocating this file to
@explanations/agent-architecture-improvements.md.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (16)
.eslintcache.gitignoreBLACKBOX.mdpackage.jsonplans/agent-architecture-improvements.mdplans/code-generation-tool-calling-fix.mdsrc/agents/agents/code-generation.tssrc/agents/retry.tssrc/app/api/generate/route.tssrc/prompts/angular.tssrc/prompts/nextjs.tssrc/prompts/react.tssrc/prompts/shared.tssrc/prompts/svelte.tssrc/prompts/vue.tstest-code-gen.ts
✅ Files skipped from review due to trivial changes (2)
- .eslintcache
- src/prompts/react.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- src/agents/retry.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.md
📄 CodeRabbit inference engine (.cursor/rules/rules.mdc)
Minimize the creation of .md files; if necessary, place them in the @explanations folder
Files:
plans/code-generation-tool-calling-fix.mdplans/agent-architecture-improvements.mdBLACKBOX.md
src/prompts/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Validate all user inputs using Zod schemas in prompts; sanitize file paths to prevent directory traversal attacks
Files:
src/prompts/svelte.tssrc/prompts/angular.tssrc/prompts/vue.tssrc/prompts/nextjs.tssrc/prompts/shared.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/prompts/svelte.tssrc/app/api/generate/route.tssrc/prompts/angular.tssrc/prompts/vue.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tssrc/prompts/shared.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/prompts/svelte.tssrc/app/api/generate/route.tssrc/prompts/angular.tssrc/prompts/vue.tstest-code-gen.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tssrc/prompts/shared.ts
src/prompts/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Framework-specific AI prompts must be maintained in
src/prompts/with separate files per framework (nextjs.ts, angular.ts, etc.)
Files:
src/prompts/svelte.tssrc/prompts/angular.tssrc/prompts/vue.tssrc/prompts/nextjs.tssrc/prompts/shared.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification
Files:
src/app/api/generate/route.ts
*.md
📄 CodeRabbit inference engine (AGENTS.md)
Documentation files should be placed in
explanations/folder, not in the root directory
Files:
BLACKBOX.md
🧠 Learnings (34)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
plans/code-generation-tool-calling-fix.mdsrc/prompts/svelte.tssrc/app/api/generate/route.tssrc/prompts/angular.tssrc/prompts/vue.tsplans/agent-architecture-improvements.mdtest-code-gen.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tsBLACKBOX.mdsrc/prompts/shared.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/prompts/*.ts : Framework-specific AI prompts must be maintained in `src/prompts/` with separate files per framework (nextjs.ts, angular.ts, etc.)
Applied to files:
plans/code-generation-tool-calling-fix.mdsrc/prompts/svelte.tssrc/prompts/angular.tssrc/prompts/vue.tsplans/agent-architecture-improvements.mdtest-code-gen.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tsBLACKBOX.mdsrc/prompts/shared.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
plans/code-generation-tool-calling-fix.mdtest-code-gen.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/framework-selector.ts : Support framework auto-detection priority: Explicit user mention → default Next.js → Enterprise indicators (Angular) → Material Design preference (Angular/Vue) → Performance critical (Svelte)
Applied to files:
src/prompts/svelte.tssrc/prompts/angular.tssrc/prompts/vue.tssrc/prompts/nextjs.tsBLACKBOX.mdsrc/prompts/shared.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/framework-selector.ts : Use Next.js 15 as default framework for web apps unless user explicitly specifies otherwise; check `src/prompts/framework-selector.ts` for detection logic
Applied to files:
src/prompts/svelte.tssrc/prompts/angular.tssrc/prompts/vue.tssrc/prompts/nextjs.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/**/*.ts : Validate all user inputs using Zod schemas in prompts; sanitize file paths to prevent directory traversal attacks
Applied to files:
src/prompts/svelte.tssrc/prompts/vue.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tssrc/prompts/shared.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/prompts/svelte.tssrc/prompts/vue.tstest-code-gen.tssrc/prompts/nextjs.tssrc/agents/agents/code-generation.tsBLACKBOX.mdsrc/prompts/shared.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/**/*.{ts,tsx} : Use modern framework patterns: Next.js App Router and React hooks
Applied to files:
src/app/api/generate/route.tssrc/prompts/nextjs.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/app/api/**/*.ts : Sync credit usage with Clerk custom claim `plan: 'pro'` for Pro tier verification
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/middleware.ts : Use Clerk authentication middleware in `src/middleware.ts` for end-to-end type safety
Applied to files:
src/app/api/generate/route.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/schema.ts : Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in `usage` table with rolling 24-hour expiration
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always use the `v.null()` validator when returning a null value from a Convex function
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/app/api/generate/route.tstest-code-gen.tssrc/agents/agents/code-generation.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to tests/**/*.{test,spec}.ts : Write all tests in `/tests/` directory using Jest patterns; include security, sanitization, and file operation tests
Applied to files:
test-code-gen.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to jest.config.js : Run tests from `/tests` directory with patterns: `**/__tests__/**/*.ts`, `**/?(*.)+(spec|test).ts`; coverage scope: `src/**/*.ts` (excludes .d.ts and /generated)
Applied to files:
test-code-gen.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Never start dev servers in E2B sandboxes - only run build and lint validation
Applied to files:
test-code-gen.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to eslint.config.mjs : Use ESLint with Flat Config format in `eslint.config.mjs` with TypeScript strict mode and Next.js plugin enabled
Applied to files:
src/prompts/nextjs.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/code-generation.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Be strict with types, particularly around IDs of documents; use `Id<'tableName'>` rather than `string` for function parameters
Applied to files:
src/agents/agents/code-generation.tsBLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/auth.config.ts : Keep OAuth tokens encrypted in Convex; never expose API keys in client-side code (use NEXT_PUBLIC_ prefix only for public values)
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/functions.ts : Update E2B template name in `src/inngest/functions.ts` (line ~22) after building new templates
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Organize files thoughtfully in the `convex/` directory using file-based routing for public query, mutation, and action functions
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/components/**/*.{ts,tsx} : Use Convex real-time database subscriptions to enable UI updates when data changes in `projects`, `messages`, `fragments`, `usage`, `oauthConnections`, and `imports` tables
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to explanations/**/*.md : Store all `.md` documentation files in `@/explanations/` directory, except for core setup files (CLAUDE.md, README.md)
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/**/*.{ts,tsx} : TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/generated/**/*.ts : Ignore `/src/generated/*` files in ESLint configuration (generated code should not be linted)
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to **/*.{ts,tsx} : Strict TypeScript usage - avoid using `any` type in code
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to next.config.ts : Configure Next.js with Turbopack in dev mode, image optimization (AVIF, WebP), CSS optimization with Critters, and strict mode enabled in `next.config.ts`
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/**/*.ts : Use Convex database (`src/api/convex/*`) for all new data operations; never use deprecated PostgreSQL schema (`prisma/`)
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/components/**/*.{ts,tsx} : Implement accessibility and responsive design in UI components
Applied to files:
BLACKBOX.md
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always add `'use node';` to the top of files containing actions that use Node.js built-in modules
Applied to files:
BLACKBOX.md
🧬 Code graph analysis (1)
src/app/api/generate/route.ts (6)
src/lib/auth-server.ts (1)
getConvexClientWithAuth(48-61)src/agents/types.ts (1)
StreamUpdate(46-53)src/agents/sandbox.ts (1)
sandboxManager(162-162)src/agents/agents/code-generation.ts (1)
generateCode(137-321)src/agents/agents/validation.ts (1)
runValidation(5-35)src/agents/agents/error-fixer.ts (1)
fixErrors(22-70)
🪛 LanguageTool
BLACKBOX.md
[style] ~527-~527: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...e) --- Last Updated: December 28, 2025 Project Version: 0.1.0 **Status...
(MISSING_COMMA_AFTER_YEAR)
🪛 markdownlint-cli2 (0.18.1)
BLACKBOX.md
8-8: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (23)
.gitignore (1)
53-57: LGTM!The re-addition of
/neon-thing/and new.blackboxclipattern follow the established conventions in your gitignore: root-level directories use leading slashes, while tool/config artifacts (like.claude,.npm) omit them. The new "AI" section is well-organized with appropriate spacing.src/prompts/shared.ts (2)
115-143: Well-structured mandatory tool usage enforcement.The prominent visual formatting with box-drawing characters and emoji effectively draws attention to the critical tool-usage requirements. The explicit DO/DON'T patterns and step-by-step workflow (lines 132-137) provide clear guidance for the AI agent.
199-233: Comprehensive security best practices section.The security guidance covers essential areas: input validation, authentication, data protection, vulnerability prevention, and API safety. This aligns well with the coding guideline requiring Zod schemas for validation and file path sanitization.
src/prompts/vue.ts (2)
4-24: Consistent mandatory action block with other framework prompts.The Vue prompt follows the same pattern as Angular, Next.js, and Svelte prompts with clear DO/DON'T guidance. This consistency helps ensure uniform AI behavior across frameworks.
28-105: Comprehensive Vue-specific guidance.The environment details, Composition API patterns, file conventions, and component structure examples provide clear guidance. The explicit note that Vuetify and Tailwind are pre-installed helps prevent redundant package installations.
src/prompts/angular.ts (2)
4-24: Consistent mandatory action block.The Angular prompt follows the established pattern across all framework prompts, ensuring uniform tool-first behavior from the AI agent.
28-87: Modern Angular patterns with standalone components.The prompt correctly emphasizes standalone components (Angular 17+ pattern), signals for reactive state, and proper Angular Material usage. The file conventions and component structure examples are well-documented.
BLACKBOX.md (1)
322-356: Well-documented agent architecture section.The new AI Agent System section clearly documents the architecture, available agents, tools, and supported frameworks. This provides good context for developers working with the new agent infrastructure.
test-code-gen.ts (1)
1-71: Test file location violates project conventions.Based on learnings, tests should be written in the
/tests/directory using Jest patterns. This file is in the root directory and uses a standalone script approach instead of Jest.Is this intentional as an ad-hoc test script, or should it be moved to the tests directory with proper Jest setup?
src/prompts/svelte.ts (2)
4-24: Consistent mandatory action block.The Svelte prompt follows the established pattern across all framework prompts, ensuring uniform tool-first behavior.
28-114: Comprehensive SvelteKit guidance with DaisyUI.The environment details, reactive patterns, file conventions, and component structure examples provide clear guidance. The explicit note that DaisyUI and Tailwind are pre-installed helps prevent redundant installations.
src/prompts/nextjs.ts (2)
4-30: Enhanced mandatory action block with behavior examples.The Next.js prompt includes additional CORRECT/INCORRECT behavior examples (lines 24-28) that provide concrete guidance. This extra detail is appropriate for the default framework.
34-82: Detailed Shadcn UI usage guidance.The prompt provides comprehensive guidance on:
- Shadcn CLI installation (
npx shadcn@latest add)- Correct import paths (
@/components/ui/*)- The
cnutility import location (@/lib/utils)- File path conversion for
readFilestoolThis level of detail helps prevent common mistakes with Shadcn component usage.
src/app/api/generate/route.ts (6)
67-78: LGTM: Appropriate sandboxId validation.The format validation correctly handles the optional
sandboxIdparameter, validating only when provided. The regex pattern appropriately restricts to alphanumeric characters, hyphens, and underscores.
51-65: LGTM: Appropriate environment validation.Early validation of required environment variables with clear error messages is good practice. The 500 status code is appropriate for server misconfiguration.
80-90: LGTM: Clean SSE setup with observability.The Sentry tagging and TransformStream setup for Server-Sent Events follows Next.js best practices. The
sendUpdatehelper provides a clean abstraction.
120-142: LGTM: Sandbox creation logic properly handles missing sandboxId.The code now correctly creates a sandbox when one isn't provided, addressing the previous issue where
sandboxIdwas undefined. The framework mapping and error handling are appropriate.
144-154: LGTM: Code generation invocation.The
generateCodecall correctly uses the guaranteed-to-existeffectiveSandboxIdand passes the streaming callback. Logging provides good observability.
198-219: LGTM: Robust error handling and SSE response.The error handling properly integrates with Sentry and sends error events over the stream. The
finallyblock ensures writer cleanup, and SSE response headers are correctly configured including theX-Accel-Buffering: noheader for proxy compatibility.src/agents/agents/code-generation.ts (4)
15-23: LGTM: Singleton Convex client initialization.The singleton pattern for the Convex HTTP client is appropriate for a module-level utility. Environment variable validation with a clear error message is good practice.
63-115: LGTM: Well-designed readFiles and terminal tools.The
readFilestool properly handles file read errors, and theterminaltool appropriately blocks dev server commands that shouldn't run in the sandbox environment. Sentry breadcrumbs provide good observability.
137-158: LGTM: Clean function setup with proper error handling.The function initialization establishes a contextual logger, sends initial progress updates, and handles sandbox connection errors with clear messaging. Good structure.
223-321: LGTM: Robust AI generation with tool enforcement and validation.The implementation enforces tool usage via
toolChoice: 'required', validates that tools were actually called and files were created, includes comprehensive logging, and uses retry logic for transient failures. The error messages are clear and actionable. This addresses the core concerns outlined in the architecture improvement plan.
| ### Core Purpose | ||
| - Generate full-stack web applications through natural language conversations |
There was a problem hiding this comment.
Add language identifier to fenced code block.
The code block on line 8 is missing a language identifier, which helps with syntax highlighting and accessibility.
🔎 Proposed fix
### Evidence from Logs
-```
+```text
[AI] Stream completeCommittable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
8-8: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In BLACKBOX.md around lines 7 to 8, the fenced code block is missing a language
identifier; update the opening backticks to include a language (e.g., "text") so
the block becomes ```text to enable proper syntax highlighting and accessibility
for the contained line "[AI] Stream complete".
| # Inngest (background jobs - optional) | ||
| INNGEST_EVENT_KEY="" | ||
| INNGEST_SIGNING_KEY="" | ||
|
|
||
| # Polar.sh (subscriptions) | ||
| POLAR_ACCESS_TOKEN="your-polar-token" | ||
| POLAR_WEBHOOK_SECRET="your-polar-webhook-secret" | ||
| # Sentry (error tracking) | ||
| NEXT_PUBLIC_SENTRY_DSN="" | ||
| SENTRY_DSN="" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for Inngest usage in the codebase (TypeScript/TSX files)
echo "=== Searching for Inngest usage ==="
rg -n "inngest" --type=ts --type=tsx -i -A 2
echo ""
echo "=== Checking BLACKBOX.md location ==="
find . -name "BLACKBOX.md" -type fRepository: Jackson57279/zapdev
Length of output: 196
🏁 Script executed:
#!/bin/bash
# Check if explanations folder exists and what docs are there
echo "=== Checking explanations folder ==="
ls -la explanations/ 2>/dev/null || echo "explanations/ folder does not exist"
echo ""
echo "=== Checking for other .md files in root ==="
find . -maxdepth 1 -name "*.md" -type f
echo ""
echo "=== Checking BLACKBOX.md content (first 20 lines) ==="
head -20 ./BLACKBOX.mdRepository: Jackson57279/zapdev
Length of output: 4871
Remove obsolete Inngest environment variables from BLACKBOX.md and move file to explanations/ folder.
Inngest is not used anywhere in the codebase; remove the environment variables from lines 82-84 entirely. Additionally, move BLACKBOX.md to the explanations/ folder per the project's documentation placement guideline.
🤖 Prompt for AI Agents
In BLACKBOX.md around lines 82 to 88, remove the obsolete Inngest environment
variable lines (INNGEST_EVENT_KEY="" and INNGEST_SIGNING_KEY="") so only Sentry
variables remain, and then relocate the BLACKBOX.md file into the explanations/
directory (update any references or README links that point to its old path if
present). Ensure the new file path is explanations/BLACKBOX.md and commit the
deletion at the original path and addition at the new path.
| console.log('✅ Test completed successfully!'); | ||
| process.exit(0); | ||
| } catch (error) { | ||
| console.error('\n\n❌ TEST FAILED:'); | ||
| console.error(error); | ||
| process.exit(1); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate the test-code-gen.ts file
find . -name "test-code-gen.ts" -type fRepository: Jackson57279/zapdev
Length of output: 81
🏁 Script executed:
# Read the test-code-gen.ts file to examine the context
cat -n ./test-code-gen.tsRepository: Jackson57279/zapdev
Length of output: 3037
🏁 Script executed:
# Find the sandboxManager definition
find . -name "sandbox*" -type f | head -20Repository: Jackson57279/zapdev
Length of output: 114
🏁 Script executed:
# Read the sandbox.ts file to check the sandboxManager API
cat -n ./src/agents/sandbox.tsRepository: Jackson57279/zapdev
Length of output: 6028
🏁 Script executed:
# Check if Sandbox object has a kill() method or similar cleanup
rg "kill\|close\|disconnect" ./src/agents/sandbox.ts -A 2Repository: Jackson57279/zapdev
Length of output: 45
🏁 Script executed:
# Check all public methods of SandboxManager class
ast-grep --pattern 'class SandboxManager {
$$$
}'Repository: Jackson57279/zapdev
Length of output: 7684
Add explicit sandbox cleanup before process exit.
The sandbox is created at line 16 but never explicitly cleaned up before process.exit() is called. While automatic cleanup exists via scheduleCacheCleanup() (5-minute timeout), this won't execute before the process terminates, leaving orphaned resources.
The proposed fix references sandboxManager.close(), but this method doesn't exist. Instead, call sandbox.kill() directly or add a public cleanup method to SandboxManager. Move the sandbox declaration outside the try block to access it in the catch block for error cleanup:
async function testCodeGeneration() {
console.log('🧪 Starting code generation test...\n');
+ let sandbox: Awaited<ReturnType<typeof sandboxManager.create>> | null = null;
try {
// Create a sandbox
console.log('📦 Creating sandbox...');
- const sandbox = await sandboxManager.create('nextjs');
+ sandbox = await sandboxManager.create('nextjs');
console.log(`✅ Sandbox created: ${sandbox.sandboxId}\n`);
// ... rest of code ...
console.log('✅ Test completed successfully!');
+ if (sandbox) {
+ await sandbox.kill();
+ }
process.exit(0);
} catch (error) {
console.error('\n\n❌ TEST FAILED:');
console.error(error);
+ if (sandbox) {
+ try {
+ await sandbox.kill();
+ } catch (cleanupError) {
+ console.error('Cleanup failed:', cleanupError);
+ }
+ }
process.exit(1);
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In test-code-gen.ts around lines 62 to 68, the sandbox created earlier (line 16)
is not explicitly cleaned up before calling process.exit(), so orphaned
resources may remain; move the sandbox declaration out of the try block so it is
in scope for both success and catch, and ensure you call sandbox.kill() (or a
new public SandboxManager.cleanup()/close() wrapper if you prefer) in both the
success path before process.exit(0) and in the catch block before
process.exit(1) so the sandbox is reliably torn down.
…57279/zapdev into feat/migrate-off-inngest
|
🔍 Analyzing PR changes and preparing to run tests... |
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/app/api/generate/route.ts (3)
13-42: Pro tier verification still missing.While the credit check logic is implemented, the coding guidelines require syncing credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification. The current implementation checks credits but doesn't verify the user's plan tier, which may be needed for differentiated rate limits or feature access.As per coding guidelines,
src/app/api/**/*.tsshould sync credit usage with Clerk custom claimplan: 'pro'for Pro tier verification.
115-118: Add explicit null check for project before accessing properties.The
projectquery result could benullif the project doesn't exist or the user lacks access. While later code uses optional chaining (project?.framework), an early explicit check would provide a clearer error response and prevent proceeding with invalid state.🔎 Proposed fix
const project = await convex.query(api.projects.getForSystem, { projectId: projectId as Id<'projects'>, }); + + if (!project) { + console.error('[GENERATE] Project not found:', projectId); + await sendUpdate({ type: 'error', error: 'Project not found' }); + await writer.close(); + return; + } console.log('[GENERATE] Project framework:', project?.framework);
156-191: Validation failure not properly surfaced when fixes fail.After the error-fixing attempt, the code unconditionally saves the fragment and marks the message as
COMPLETE, even ifvalidation.successremainsfalse. This masks build/lint failures from users who receive a "complete" event for code that may not compile.🔎 Proposed fix
if (hasFiles) { await sendUpdate({ type: 'status', message: 'Validating code...' }); let validation = await runValidation(effectiveSandboxId); if (!validation.success) { console.log('[GENERATE] Validation failed, attempting fixes with ErrorFixerAgent'); await sendUpdate({ type: 'status', message: 'Fixing errors...' }); validation = await fixErrorsWithAgent(effectiveSandboxId, validation.errors || [], 0, sendUpdate); } else { console.log('[GENERATE] Validation passed'); } + + // Surface validation failure if fixes didn't succeed + if (!validation.success) { + console.warn('[GENERATE] Validation still failing after fix attempts:', validation.errors); + await sendUpdate({ + type: 'error', + error: `Code validation failed: ${validation.errors?.join('; ') || 'Unknown errors'}`, + }); + // Still save fragment but with failed status indicator + } } else { console.log('[GENERATE] No files generated, skipping validation'); }
🧹 Nitpick comments (3)
src/app/api/generate/route.ts (1)
92-113: SecondgetConvexClientWithAuth()call creates redundant client.Line 97 creates another Convex client inside the async IIFE, but one was already created at line 20. Consider reusing the outer
convexinstance to avoid redundant authentication overhead.🔎 Proposed fix
(async () => { try { console.log('[GENERATE] Starting code generation for project:', projectId); let assistantMessageId: Id<'messages'>; - const convex = await getConvexClientWithAuth(); - if (messageId) {src/agents/agents/code-agent.ts (1)
267-273: Consider warning when no files are generated.The function returns successfully even when
filesis empty, which could indicate the AI didn't use the file tools. While this may be intentional for text-only responses, it could also indicate a failure mode that should be logged or flagged.🔎 Proposed fix
+ if (Object.keys(files).length === 0) { + console.warn('[Agent] No files generated - AI may not have used file tools'); + } + logger.complete({ filesCount: Object.keys(files).length }); return { summary, files, steps: steps.length, };src/agents/agents/error-fixer-agent.ts (1)
89-109: Missing dev server command blocking.The
terminaltool incode-agent.tsblocksnpm run devandnpm startcommands, but this error-fixer version doesn't have the same safeguard. Consider adding consistency to prevent accidental dev server starts during error fixing.🔎 Proposed fix
terminal: tool({ description: 'Run terminal commands to verify fixes (npm run lint, npm run build).', inputSchema: z.object({ command: z.string().describe('Command to run'), timeoutMs: z.number().optional().describe('Timeout in milliseconds'), }), execute: async ({ command, timeoutMs = 60000 }) => { Sentry.addBreadcrumb({ category: 'error-fixer', message: `Running verification: ${command}`, }); + // Prevent starting dev servers + if (command.includes('npm run dev') || command.includes('npm start')) { + return { + error: 'Cannot start dev servers in sandbox. The preview is automatically available.', + }; + } + const result = await sandbox.commands.run(command, { timeoutMs: timeoutMs ?? 60000 });
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.tssrc/agents/index.tssrc/app/api/generate/route.ts
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/agents/agents/code-agent.tssrc/agents/index.tssrc/app/api/generate/route.tssrc/agents/agents/error-fixer-agent.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/agents/agents/code-agent.tssrc/agents/index.tssrc/app/api/generate/route.tssrc/agents/agents/error-fixer-agent.ts
src/app/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Sync credit usage with Clerk custom claim
plan: 'pro'for Pro tier verification
Files:
src/app/api/generate/route.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Implement message flow: User input → Convex `messages` table → Inngest `code-agent/run` → Code generation → `fragments` table → Real-time UI updates
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/agents/agents/code-agent.tssrc/agents/index.tssrc/app/api/generate/route.tssrc/agents/agents/error-fixer-agent.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/agents/code-agent.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/agents/agents/code-agent.tssrc/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/**/*.{ts,tsx} : Use modern framework patterns: Next.js App Router and React hooks
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/app/api/**/*.ts : Sync credit usage with Clerk custom claim `plan: 'pro'` for Pro tier verification
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/middleware.ts : Use Clerk authentication middleware in `src/middleware.ts` for end-to-end type safety
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to convex/schema.ts : Configure credit system: Free tier (5 generations/24h), Pro tier (100 generations/24h), tracked in `usage` table with rolling 24-hour expiration
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Always use the `v.null()` validator when returning a null value from a Convex function
Applied to files:
src/app/api/generate/route.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/agents/agents/error-fixer-agent.ts
🧬 Code graph analysis (3)
src/agents/agents/code-agent.ts (4)
tests/mocks/convex-browser.ts (1)
ConvexHttpClient(1-11)tests/mocks/e2b-code-interpreter.ts (1)
Sandbox(1-37)src/agents/logger.ts (1)
error(47-60)src/agents/types.ts (2)
GenerationRequest(27-33)StreamUpdate(46-53)
src/app/api/generate/route.ts (5)
src/lib/auth-server.ts (1)
getConvexClientWithAuth(48-61)src/agents/types.ts (2)
StreamUpdate(46-53)Framework(3-3)src/agents/index.ts (4)
sandboxManager(6-6)generateCodeWithAgent(21-21)runValidation(17-17)fixErrorsWithAgent(23-23)src/agents/sandbox.ts (1)
sandboxManager(162-162)src/agents/agents/code-agent.ts (1)
generateCodeWithAgent(167-280)
src/agents/agents/error-fixer-agent.ts (4)
tests/mocks/e2b-code-interpreter.ts (1)
Sandbox(1-37)src/agents/logger.ts (1)
error(47-60)src/agents/index.ts (1)
createErrorFixerAgent(23-23)src/agents/types.ts (2)
StreamUpdate(46-53)ValidationResult(40-44)
🔇 Additional comments (8)
src/app/api/generate/route.ts (1)
211-219: LGTM!The SSE response configuration is well-structured with appropriate headers for streaming, including
X-Accel-Buffering: 'no'for nginx compatibility.src/agents/agents/code-agent.ts (3)
147-162: LGTM!The
createCodeAgentfactory is clean and properly configures theToolLoopAgentwith framework-specific prompts, model selection, and a reasonable step limit of 15.
285-295: LGTM!The
extractSummaryfunction provides a sensible fallback chain: first checking for structured<task_summary>tags, then falling back to the first paragraph with a 200-character limit.
14-23: The unauthenticated Convex client is correct for this usage.The
getForSystemquery inconvex/projects.tsexplicitly bypasses authentication with the comment "This bypasses authentication since Inngest is a trusted system." No authentication is needed or expected for this call, so the lazy-initializedConvexHttpClientwithout auth is the appropriate choice here. No permission issues will occur.Likely an incorrect or invalid review comment.
src/agents/agents/error-fixer-agent.ts (3)
11-32: LGTM!The error-fixer instructions are well-structured with clear guidance for the AI. The
MAX_FIX_ATTEMPTS = 2aligns with the auto-fix retry logic documented in the codebase learnings.
187-214: LGTM!The streaming execution and recursive retry logic is well-implemented. The recursive call with incremented
attemptensures the MAX_FIX_ATTEMPTS guard works correctly, and success/failure paths are clearly handled.
156-162: No issues found. Thelogger.warn()method is defined on theAgentLoggerclass and is correctly used at line 157.src/agents/index.ts (1)
1-23: LGTM!The barrel file is well-organized with clear separation between core utilities, legacy exports (for backward compatibility), and new ToolLoopAgent-based exports. Type exports are properly handled using
export type.
|
🔍 Analyzing PR changes and preparing to run tests... |
|
🚀 Launching Scrapybara desktop... |
|
❌ Something went wrong: |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (8)
src/agents/agents/code-agent.ts (3)
124-140: Shell injection risk in listFiles tool.The path escaping only handles backslashes and double quotes but doesn't protect against shell metacharacters like
$(), backticks, or other injection vectors. This was previously flagged and remains unaddressed.As per previous review, consider using a safe escaping library (e.g.,
shell-quote) or validating the path with a strict regex pattern.
192-196: Validate projectId before casting to Id<'projects'>.The
projectIdis cast toId<'projects'>without validation. If the ID is malformed, the Convex query will fail with an unclear error message.🔎 Proposed fix
+ // Validate projectId format + if (!request.projectId || typeof request.projectId !== 'string') { + throw new Error('Invalid projectId: must be a non-empty string'); + } + const project = await getConvex().query(api.projects.getForSystem, { projectId: request.projectId as Id<'projects'>, });As per coding guidelines, be strict with types, particularly around IDs of documents.
210-212: onProgress callback should be awaited in onFileWrite.The
onProgresscallback returnsPromise<void>but isn't awaited when called from theonFileWritecallback. This could lead to unhandled promise rejections or race conditions.🔎 Proposed fix
const agent = createCodeAgent( sandbox, framework, request.model as ModelId, files, - (path) => { - onProgress({ type: 'file', filePath: path }); + async (path) => { + await onProgress({ type: 'file', filePath: path }); } );Note: This also requires updating the
onFileWriteparameter type increateSandboxToolsto accept(path: string) => void | Promise<void>and awaiting it in the tool execution.src/agents/agents/code-generation.ts (5)
15-23: Race condition in lazy Convex client initialization.This has the same race condition issue as in
src/agents/agents/code-agent.tslines 14-23. Multiple concurrent calls could create multipleConvexHttpClientinstances.Consider extracting the Convex client to a shared singleton module with proper initialization.
117-133: Shell injection risk in listFiles tool.The path escaping is insufficient to prevent shell injection. This was flagged in previous reviews and remains unaddressed.
As per previous review, use proper shell escaping or path validation to prevent injection attacks.
167-169: Validate projectId before casting to Id<'projects'>.The
projectIdis cast toId<'projects'>without validation. This was previously flagged and should be addressed.As per coding guidelines and previous review comments, validate the projectId format before casting.
177-179: onProgress callback should be awaited.The
onProgresscallback returnsPromise<void>but isn't awaited. This was previously flagged.🔎 Proposed fix
- const tools = createAgentTools(sandbox, files, (path) => { - onProgress({ type: 'file', filePath: path }); + const tools = createAgentTools(sandbox, files, async (path) => { + await onProgress({ type: 'file', filePath: path }); });Also update the
onFileWriteparameter type increateAgentToolsto accept promises.
184-191: Validate model ID before casting to ModelId.The
request.modelis cast toModelIdwithout validation. This was previously flagged.Consider validating against allowed ModelId values before casting.
🧹 Nitpick comments (2)
src/agents/agents/error-fixer-agent.ts (2)
89-109: Remove redundant nullish coalescing operator.Line 95 already provides a default value of
60000for thetimeoutMsparameter, making the nullish coalescing operator?? 60000on line 101 redundant.🔎 Proposed simplification
execute: async ({ command, timeoutMs = 60000 }) => { Sentry.addBreadcrumb({ category: 'error-fixer', message: `Running verification: ${command}`, }); - const result = await sandbox.commands.run(command, { timeoutMs: timeoutMs ?? 60000 }); + const result = await sandbox.commands.run(command, { timeoutMs }); return { stdout: result.stdout || '', stderr: result.stderr || '', exitCode: result.exitCode ?? 0, }; },
216-216: Use logger instead of console.log for consistency.Line 216 uses
console.logdirectly, which is inconsistent with the rest of the codebase that uses theloggerinstance created on line 155.🔎 Proposed fix
- console.log(`[ErrorFixer] Completed in ${steps.length} steps`); + logger.progress('complete', `Completed in ${steps.length} steps`);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/agents/agents/code-agent.tssrc/agents/agents/code-generation.tssrc/agents/agents/error-fixer-agent.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript strict mode enabled in ESLint with no-explicit-any (warn) and no-unused-vars (error, except underscore-prefixed)
Use modern framework patterns: Next.js App Router and React hooks
Files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Strict TypeScript usage - avoid using
anytype in code
Files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Use Inngest for background job orchestration and AI agent workflows
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : AI code generation agents must follow framework-specific prompts from `src/prompts/` directory
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to sandbox-templates/**/* : Build E2B sandbox templates for each framework (Next.js, Angular, React, Vue, Svelte) with Docker before running AI code generation
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/inngest/functions.ts : Use Inngest 3.44 for job orchestration with `code-agent/run` function and auto-fix retry logic (max 2 attempts on lint/build errors)
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.tssrc/agents/agents/error-fixer-agent.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/prompts/*.ts : Framework-specific AI prompts must be maintained in `src/prompts/` with separate files per framework (nextjs.ts, angular.ts, etc.)
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Always run `bun run lint` and `bun run build` for validation in sandboxes after code generation
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.ts
📚 Learning: 2025-12-14T11:08:17.520Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-14T11:08:17.520Z
Learning: Applies to convex/**/*.{ts,tsx} : Be strict with types, particularly around IDs of documents; use `Id<'tableName'>` rather than `string` for function parameters
Applied to files:
src/agents/agents/code-generation.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to src/prompts/**/*.ts : Validate all user inputs using Zod schemas in prompts; sanitize file paths to prevent directory traversal attacks
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/code-agent.ts
📚 Learning: 2025-12-14T11:07:46.225Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T11:07:46.225Z
Learning: Applies to sandbox-templates/**/*.{ts,tsx,js,jsx,vue,svelte,html,css} : Run `bun run lint && bun run build` for validation; auto-fix logic detects SyntaxError, TypeError, and Build failed patterns with max 2 retry attempts
Applied to files:
src/agents/agents/code-generation.tssrc/agents/agents/error-fixer-agent.ts
📚 Learning: 2025-12-14T11:08:35.008Z
Learnt from: CR
Repo: Jackson57279/zapdev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T11:08:35.008Z
Learning: Applies to src/inngest/**/*.{ts,tsx} : Never start dev servers in E2B sandboxes - only run build and lint validation
Applied to files:
src/agents/agents/code-generation.ts
🧬 Code graph analysis (3)
src/agents/agents/code-generation.ts (6)
src/agents/logger.ts (2)
error(47-60)createLogger(78-80)src/agents/types.ts (3)
GenerationRequest(27-33)StreamUpdate(46-53)Framework(3-3)src/agents/sandbox.ts (1)
sandboxManager(162-162)src/agents/client.ts (2)
getModel(68-71)ModelId(66-66)src/agents/retry.ts (2)
withRetry(23-64)retryOnTransient(78-87)src/agents/prompts/index.ts (1)
getFrameworkPrompt(29-31)
src/agents/agents/code-agent.ts (2)
src/agents/logger.ts (1)
error(47-60)src/agents/types.ts (2)
GenerationRequest(27-33)StreamUpdate(46-53)
src/agents/agents/error-fixer-agent.ts (3)
tests/mocks/e2b-code-interpreter.ts (1)
Sandbox(1-37)src/agents/logger.ts (1)
error(47-60)src/agents/types.ts (2)
StreamUpdate(46-53)ValidationResult(40-44)
🔇 Additional comments (9)
src/agents/agents/error-fixer-agent.ts (7)
1-9: LGTM! Clean imports with proper typing.The imports are well-structured, use named exports, and maintain strict TypeScript typing without any
anytypes.
11-32: LGTM! Well-structured error-fixing instructions and retry limit.The prompt provides clear guidance for the AI agent, and
MAX_FIX_ATTEMPTS = 2aligns with the previous retry logic.
111-127: LGTM! Secure file listing with proper path escaping.The tool correctly escapes the path for shell safety, uses null-separated output to handle filenames with spaces, and limits results to prevent overwhelming the agent.
134-144: LGTM! Proper agent configuration with safety limits.The
stopWhen: stepCountIs(10)prevents infinite loops, andtoolChoice: 'required'ensures the agent actively uses tools for error fixing.
173-187: Great improvement! Sandbox connection error handling now implemented.This addresses the previous review comment about missing error handling for sandbox connections. The implementation includes:
- Comprehensive try-catch around the connection
- Detailed error wrapping with context (sandboxId, operation)
- Proper logging with the logger instance
- Sentry integration for error tracking
- Graceful failure with meaningful error messages
203-240: LGTM! Solid error-fixing orchestration with streaming and retry logic.The function correctly:
- Streams AI responses in real-time through
onProgress- Re-validates after fixes are applied
- Recursively retries with incremented attempt counter (protected by MAX_FIXER_ATTEMPTS check)
- Handles errors gracefully with Sentry integration
1-241: Informational: Architectural shift from Inngest to direct agent execution.Based on learnings, this replaces the previous Inngest-based orchestration pattern. The new architecture uses direct
ToolLoopAgentexecution with streaming support, which aligns with the PR objectives to migrate away from Inngest. The retry logic (MAX_FIX_ATTEMPTS = 2) is preserved from the previous implementation.Note: Retrieved learnings about Inngest usage are now outdated given this migration.
src/agents/agents/code-generation.ts (2)
193-218: Well-implemented conversation history handling with tool priming.The conversation history is built without mutating the input, and the tool priming message (lines 207-212) is a smart technique to reinforce tool usage in follow-up conversations. This should help prevent the "model did not use any tools" errors.
285-299: Excellent defensive validation for tool usage.These checks ensure the AI model actually used tools and created files, preventing silent failures. The clear error messages help with debugging and user feedback. This is a critical safeguard for the code generation workflow.
| // Lazy Convex client initialization | ||
| let _convex: ConvexHttpClient | null = null; | ||
| function getConvex(): ConvexHttpClient { | ||
| if (!_convex) { | ||
| const url = process.env.NEXT_PUBLIC_CONVEX_URL; | ||
| if (!url) throw new Error('NEXT_PUBLIC_CONVEX_URL is not set'); | ||
| _convex = new ConvexHttpClient(url); | ||
| } | ||
| return _convex; | ||
| } |
There was a problem hiding this comment.
Race condition in lazy Convex client initialization.
The singleton pattern here is not thread-safe for concurrent requests. If multiple calls to getConvex() occur before _convex is initialized, multiple ConvexHttpClient instances could be created.
🔎 Proposed fix using a simple lock pattern
-let _convex: ConvexHttpClient | null = null;
-function getConvex(): ConvexHttpClient {
+let _convex: ConvexHttpClient | null = null;
+let _convexPromise: Promise<ConvexHttpClient> | null = null;
+
+function getConvex(): ConvexHttpClient {
if (!_convex) {
+ if (!_convexPromise) {
+ _convexPromise = (async () => {
- const url = process.env.NEXT_PUBLIC_CONVEX_URL;
- if (!url) throw new Error('NEXT_PUBLIC_CONVEX_URL is not set');
- _convex = new ConvexHttpClient(url);
+ const url = process.env.NEXT_PUBLIC_CONVEX_URL;
+ if (!url) throw new Error('NEXT_PUBLIC_CONVEX_URL is not set');
+ _convex = new ConvexHttpClient(url);
+ return _convex;
+ })();
+ }
+ // For synchronous usage, this assumes the client is already initialized
+ // Consider making getConvex async if concurrent first-calls are expected
}
return _convex;
}Alternatively, consider moving Convex client initialization to a dedicated module with proper async initialization.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Generated with Claude Code
Co-Authored-By: Claude Haiku 4.5 noreply@anthropic.com
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.