Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a Smart Filing feature: Drive OAuth flows (Google/OneDrive), drive providers, document extraction, AI-driven filing pipeline, DB models/migrations, server actions and APIs, extensive UI for setup/preview/activity, background webhook processing, token encryption, and tests. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant AuthAPI as /api/{google,outlook}/drive/auth-url
participant OAuthProvider as Google/Microsoft
participant CallbackAPI as /api/{google,outlook}/drive/callback
participant Redis as Redis (lock)
participant DB as Database
participant BrowserUI as Browser (redirect)
User->>Browser: Click "Connect Drive"
Browser->>AuthAPI: GET auth-url
AuthAPI->>AuthAPI: generate state cookie
AuthAPI-->>Browser: { url }
Browser->>OAuthProvider: redirect to consent
OAuthProvider-->>CallbackAPI: redirect with code & state
CallbackAPI->>Redis: acquire lock(code)
CallbackAPI->>CallbackAPI: validate state, exchange code
CallbackAPI->>DB: upsert DriveConnection (encrypt tokens)
CallbackAPI->>Redis: release lock
CallbackAPI-->>BrowserUI: redirect success
BrowserUI->>User: "Drive connected"
sequenceDiagram
participant Webhook
participant EmailProvider
participant FilingEngine
participant Extraction as Extractor
participant LLM
participant DriveProvider
participant DB
participant Notification
Webhook->>EmailProvider: fetch message with attachment
EmailProvider-->>FilingEngine: message + attachment
FilingEngine->>Extraction: extract text
Extraction-->>FilingEngine: extracted text
FilingEngine->>LLM: analyze document -> action/folder/confidence
alt High confidence
FilingEngine->>DriveProvider: create folders & upload file
DriveProvider-->>FilingEngine: fileId
FilingEngine->>DB: create DocumentFiling(status=FILED)
FilingEngine->>Notification: send "Filed" email
else Low confidence
FilingEngine->>DB: create DocumentFiling(status=PENDING)
FilingEngine->>Notification: send "Ask" email (filebot reply-to)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Add Smart Filing with Google Drive and OneDrive, including OAuth flows, token refresh, document extraction limits (PDF ≤32MB and ≤100 pages), preview APIs, and a drive settings UI in apps/web/app/(app)/[emailAccountId]/drive/page.tsxIntroduce drive providers ( 📍Where to StartStart with the unified drive OAuth callback handler Macroscope summarized 7270769. |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (15)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (1)
33-35: Suggest enriching error context withemailAccountId.The error handling correctly uses
captureExceptionwith descriptive context. However, sinceemailAccountIdis already available in the component scope andcaptureExceptionaccepts it in the context parameter, consider including it for better debugging capability in production.🔎 Proposed enhancement
For the Google Calendar handler:
- captureException(error, { - extra: { context: "Google Calendar OAuth initiation" }, - }); + captureException(error, { + emailAccountId, + extra: { context: "Google Calendar OAuth initiation" }, + });For the Microsoft Calendar handler:
- captureException(error, { - extra: { context: "Microsoft Calendar OAuth initiation" }, - }); + captureException(error, { + emailAccountId, + extra: { context: "Microsoft Calendar OAuth initiation" }, + });Also applies to: 60-62
apps/web/utils/ai/document-filing/analyze-document.ts (3)
1-5: Missing scoped logger for LLM feature.Per coding guidelines for LLM features, this function should use
createScopedLoggerfrom@/utils/loggerfor logging inputs and outputs with appropriate log levels. Based on learnings, LLM feature functions must import and use descriptive scoped loggers.🔎 Suggested fix
import { z } from "zod"; import type { EmailAccountWithAI } from "@/utils/llms/types"; import { getModel } from "@/utils/llms/model"; import { createGenerateObject } from "@/utils/llms"; import { cleanExtractedText } from "@/utils/drive/document-extraction"; +import { createScopedLogger } from "@/utils/logger"; + +const logger = createScopedLogger("document-filing");
43-70: Consider adding input validation with early returns.Per coding guidelines for LLM features, functions should implement input validation with early returns for invalid inputs. This prevents wasted API calls and provides better error handling.
🔎 Suggested validation
export async function analyzeDocument({ emailAccount, email, attachment, folders, }: { emailAccount: EmailAccountWithAI & { filingPrompt: string }; email: EmailContext; attachment: AttachmentContext; folders: DriveFolder[]; }): Promise<DocumentAnalysisResult> { + if (!attachment.content?.trim()) { + logger.warn("Empty attachment content, skipping analysis"); + return { + action: "use_existing" as const, + confidence: 0, + reasoning: "No document content to analyze", + }; + } + + logger.debug("Analyzing document", { + filename: attachment.filename, + subject: email.subject, + folderCount: folders.length, + }); + const modelOptions = getModel(emailAccount.user, "economy");
7-31: Consider adding Zod refinement for conditional field validation.The schema allows invalid states where
action: "use_existing"could have nofolderId, oraction: "create_new"could have nofolderPath. A Zod refinement would enforce these constraints at runtime.🔎 Suggested refinement
const documentAnalysisSchema = z.object({ action: z .enum(["use_existing", "create_new"]) .describe("Whether to use an existing folder or create a new one."), folderId: z .string() .optional() .describe( "Required if action is 'use_existing'. The ID of the existing folder from the provided list.", ), folderPath: z .string() .optional() .describe( "Required if action is 'create_new'. The path for the new folder to create.", ), confidence: z .number() .min(0) .max(1) .describe("Confidence score from 0 to 1. Use 0.9+ only when very certain."), reasoning: z .string() .describe("Brief explanation for why this folder was chosen."), -}); +}).refine( + (data) => { + if (data.action === "use_existing") return !!data.folderId; + if (data.action === "create_new") return !!data.folderPath; + return true; + }, + { + message: "folderId required for use_existing, folderPath required for create_new", + }, +);apps/web/app/(app)/[emailAccountId]/drive/ConnectDrive.tsx (1)
17-42: Remove unnecessary Content-Type header for GET requests.The
Content-Typeheader on lines 23 and 50 is not needed for GET requests, as they don't send a request body. This applies to both the Google and Microsoft connection handlers.🔎 Proposed fix
const response = await fetchWithAccount({ url: "/api/google/drive/auth-url", emailAccountId, - init: { headers: { "Content-Type": "application/json" } }, });Apply the same change to the Microsoft handler on line 50.
apps/web/utils/drive/provider.ts (1)
39-40: TODO reminder: Add token refresh support.The TODO indicates future work for handling token expiration in long-running operations. Consider tracking this as a follow-up task if not already planned.
Would you like me to open an issue to track this enhancement?
apps/web/utils/actions/drive.ts (1)
8-30: Consider defense-in-depth for the delete operation.The current implementation validates ownership with
findFirstbefore deleting, which is correct. However, the delete operation only usesconnectionIdin the where clause (line 25).For defense-in-depth against potential race conditions, consider including
emailAccountIdin the delete where clause:🔎 Suggested improvement
await prisma.driveConnection.delete({ - where: { id: connectionId }, + where: { + id: connectionId, + emailAccountId, + }, });Note: Prisma may require a compound unique constraint for this syntax. Alternatively, you could use
deleteManywhich accepts multiple conditions without requiring unique constraints.apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx (1)
50-55: Consider awaiting the disconnect action before revalidating.The current implementation doesn't wait for the disconnect action to complete before calling
mutate(). This could cause a race condition where the UI refreshes before the server has finished deleting the connection.🔎 Suggested improvement
const handleDisconnect = async () => { if (confirm("Are you sure you want to disconnect this drive?")) { - executeDisconnect({ connectionId: connection.id }); - mutate(); + const result = await executeDisconnect({ connectionId: connection.id }); + if (result?.data?.success) { + mutate(); + } } };This ensures the mutation only triggers after successful deletion and provides an opportunity to handle errors gracefully.
apps/web/utils/drive/client.ts (1)
145-152: Consider defining a type for the Microsoft token response.The
tokensobject from the JSON response is untyped, requiring type assertions on each property. A typed interface would improve type safety and maintainability.🔎 Suggested type definition
interface MicrosoftTokenResponse { access_token: string; refresh_token: string; expires_in?: number; token_type: string; scope: string; }apps/web/utils/drive/oauth-callback-helpers.ts (1)
16-26: LGTM!Clean type definitions for the OAuth flow. Note that
DriveOAuthStateis also defined inapps/web/utils/drive/types.ts- consider importing from there to avoid duplication.apps/web/utils/drive/providers/microsoft.ts (3)
22-34: Logger.with()usage at constructor level conflicts with coding guidelines.Per coding guidelines,
.with()should only be used within specific functions for context enrichment, not for global/file-level loggers. The logger instance here is stored at the class level.Consider passing context directly in each log call instead:
🔎 Suggested refactor
constructor(accessToken: string, logger?: Logger) { this.accessToken = accessToken; - this.logger = (logger || createScopedLogger("onedrive-provider")).with({ - provider: "microsoft", - }); + this.logger = logger || createScopedLogger("onedrive-provider"); this.client = Client.init({ authProvider: (done) => { done(null, this.accessToken); }, defaultVersion: "v1.0", }); }Then include
provider: "microsoft"in individual log calls where needed.
132-163: Consider tracking the large file upload limitation.The 4MB limit and TODO for resumable upload are acknowledged. Would you like me to open an issue to track implementing resumable upload for large files?
194-218: Non-null assertions onitem.idcould fail for edge cases.Both
convertToFolder(line 196) andconvertToFile(line 208) useitem.id!which assumesidis always present. While Graph API typically returns IDs, a defensive check would be safer.🔎 Suggested defensive approach
private convertToFolder(item: DriveItem): DriveFolder { + if (!item.id) { + throw new Error("DriveItem missing required id field"); + } return { - id: item.id!, + id: item.id, name: item.name || "Untitled", parentId: item.parentReference?.id ?? undefined, path: item.parentReference?.path ? `${item.parentReference.path}/${item.name}` : undefined, webUrl: item.webUrl ?? undefined, }; }Apply the same pattern to
convertToFile.apps/web/utils/drive/providers/google.ts (2)
26-41: Logger.with()usage at constructor level.Same as the OneDrive provider - per coding guidelines,
.with()should only be used within specific functions for context enrichment, not for class-level loggers.
196-217: Non-null assertions onfile.idcould fail for edge cases.Same as the OneDrive provider - both
convertToFolder(line 198) andconvertToFile(line 209) usefile.id!. Consider adding defensive checks.
apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/drive/document-extraction.ts">
<violation number="1" location="apps/web/utils/drive/document-extraction.ts:40">
P2: The `application/msword` MIME type is listed in `EXTRACTABLE_MIME_TYPES` but has no corresponding handler in the switch statement, causing `isExtractableMimeType()` to return `true` while `extractTextFromDocument()` returns `null`. Either remove `.doc` from the supported types or add explicit handling.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
There was a problem hiding this comment.
3 issues found across 32 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/drive/handle-drive-callback.ts">
<violation number="1" location="apps/web/utils/drive/handle-drive-callback.ts:137">
P1: The `await clearOAuthCode(code)` call should not mask the original error if it fails. Add `.catch()` to prevent cleanup failures from overriding the actual error handling.</violation>
</file>
<file name="apps/web/utils/ai/document-filing/analyze-document.ts">
<violation number="1" location="apps/web/utils/ai/document-filing/analyze-document.ts:7">
P2: The Zod schema doesn't enforce that `folderId` is required when `action` is `'use_existing'` and `folderPath` is required when `action` is `'create_new'`. Consider using `.refine()` to add conditional validation, otherwise the LLM might return invalid combinations.</violation>
</file>
<file name="apps/web/utils/drive/oauth-callback-helpers.ts">
<violation number="1" location="apps/web/utils/drive/oauth-callback-helpers.ts:148">
P2: Race condition: Use Prisma's `upsert` instead of `findFirst` + `update`/`create` pattern. The schema has a unique constraint on `[emailAccountId, provider]`, so `upsert` can atomically handle this in a single query, avoiding race conditions and duplicate records.</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
apps/web/utils/ai/document-filing/analyze-document.ts (2)
35-45: LGTM!The refinement correctly enforces conditional requirements, addressing previous concerns about invalid state combinations.
103-145: Escape user data before interpolating into XML tags.Special characters (
<,>,&, quotes) infilename,subject,sender, and folder attributes can break XML structure, enabling prompt injection. Escape these values or use a different delimiter strategy.🔎 Proposed XML escaping utility
+function escapeXml(unsafe: string): string { + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + function buildPrompt({ email, attachment, folders, }: { email: EmailContext; attachment: AttachmentContext; folders: DriveFolder[]; }): string { const cleanedText = cleanExtractedText(attachment.content); const truncatedText = cleanedText.length > 8000 ? `${cleanedText.slice(0, 8000)}\n\n[... document truncated ...]` : cleanedText; const foldersText = folders.length > 0 ? folders .map( (f) => - `<folder id="${f.id}" path="${f.path}" provider="${f.driveProvider}" />`, + `<folder id="${escapeXml(f.id)}" path="${escapeXml(f.path)}" provider="${escapeXml(f.driveProvider)}" />`, ) .join("\n") : "No existing folders found."; return `Decide where to file this document: <document_metadata> -<filename>${attachment.filename}</filename> -<email_subject>${email.subject}</email_subject> -<email_sender>${email.sender}</email_sender> +<filename>${escapeXml(attachment.filename)}</filename> +<email_subject>${escapeXml(email.subject)}</email_subject> +<email_sender>${escapeXml(email.sender)}</email_sender> </document_metadata> <document_content> ${truncatedText} </document_content> <existing_folders> ${foldersText} </existing_folders> Based on the user's filing preferences and the document content, decide where this document should be filed.`; }As per coding guidelines for LLM prompts, structure data safely to prevent injection.
apps/web/utils/drive/client.ts (1)
80-86: Addprompt: "consent"to Microsoft OAuth URL.Without
prompt: "consent", re-authorization may not return a refresh token, causingexchangeMicrosoftDriveCodeto throw an error at Line 125. This is critical for refresh token flows.🔎 Proposed fix
const params = new URLSearchParams({ client_id: env.MICROSOFT_CLIENT_ID, response_type: "code", redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/drive/callback`, scope: MICROSOFT_DRIVE_SCOPES.join(" "), + prompt: "consent", state, });
🧹 Nitpick comments (4)
apps/web/utils/redis/oauth-code.ts (1)
55-66: Well-documented silent error handling for cleanup operations.The implementation correctly handles cleanup errors silently, which is appropriate for this use case. The JSDoc and inline comments clearly explain the design intent.
If you want to aid debugging without changing the fail-silently behavior, you could optionally accept a logger parameter to record cleanup failures while still preventing them from bubbling up:
🔎 Optional enhancement to log silent failures
-export async function clearOAuthCode(code: string): Promise<void> { +import type { Logger } from "next-axiom"; + +export async function clearOAuthCode( + code: string, + logger?: Logger +): Promise<void> { try { await redis.del(getCodeKey(code)); - } catch { + } catch (error) { // Silently ignore - this is called in error handlers where we don't want // cleanup failures to mask the original error + logger?.error("Failed to clear OAuth code from Redis", { error }); } }apps/web/utils/filebot/is-filebot-email.ts (1)
44-48: Validation properly implemented.The function now correctly validates
userEmailformat and throws a descriptive error for invalid input, addressing previous concerns.Consider adding validation for the
tokenparameter to ensure it matches the expected format[a-zA-Z0-9]+. This would catch potential issues early if the token is malformed.🔎 Optional: Add token validation
export function getFilebotEmail({ userEmail, token, }: { userEmail: string; token: string; }): string { const [localPart, domain] = userEmail.split("@"); if (!localPart || !domain) { throw new Error("Invalid email format"); } + if (!token || !/^[a-zA-Z0-9]+$/.test(token)) { + throw new Error("Invalid token format"); + } return `${localPart}+${FILEBOT_PREFIX}-${token}@${domain}`; }apps/web/utils/drive/oauth-callback-helpers.ts (1)
61-68: Consider timing-safe comparison for state validation.While unlikely to be exploited in this context, security best practices recommend using a timing-safe comparison for OAuth state tokens to prevent timing attacks.
🔎 Proposed enhancement
import { timingSafeEqual } from "crypto"; // In validateOAuthCallback: const stateMatches = storedState && receivedState && storedState.length === receivedState.length && timingSafeEqual(Buffer.from(storedState), Buffer.from(receivedState)); if (!storedState || !receivedState || !stateMatches) { logger.warn("Invalid state during drive callback", { receivedState, hasStoredState: !!storedState, }); redirectUrl.searchParams.set("error", "invalid_state"); throw new RedirectError(redirectUrl, response.headers); }apps/web/utils/drive/document-extraction.ts (1)
117-119: Remove@ts-expect-errorand use direct import forunpdfinstead.The unpdf package includes proper TypeScript type definitions with "./dist/index.d.ts". Rather than relying on dynamic import with type suppression, use a direct import of getDocumentProxy from unpdf like other functions in the codebase. If the package is not installed, install it first, then replace the dynamic import and
@ts-expect-errorcomment with a standard import to leverage the library's native type support.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx(1 hunks)apps/web/utils/ai/document-filing/analyze-document.ts(1 hunks)apps/web/utils/drive/client.ts(1 hunks)apps/web/utils/drive/document-extraction.ts(1 hunks)apps/web/utils/drive/oauth-callback-helpers.ts(1 hunks)apps/web/utils/filebot/is-filebot-email.test.ts(1 hunks)apps/web/utils/filebot/is-filebot-email.ts(1 hunks)apps/web/utils/redis/oauth-code.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/utils/filebot/is-filebot-email.test.ts
- apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/redis/oauth-code.tsapps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.tsapps/web/utils/drive/oauth-callback-helpers.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
LLM-related code must be organized in specific directories:
apps/web/utils/ai/for main implementations,apps/web/utils/llms/for core utilities and configurations, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/utils/ai/document-filing/analyze-document.ts
apps/web/utils/ai/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.ts: LLM feature functions must import fromzodfor schema validation, usecreateScopedLoggerfrom@/utils/logger,chatCompletionObjectandcreateGenerateObjectfrom@/utils/llms, and importEmailAccountWithAItype from@/utils/llms/types
LLM feature functions must follow a standard structure: accept options withinputDataandemailAccountparameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and usecreateGenerateObjectto execute the LLM call
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures usingwithRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Files:
apps/web/utils/ai/document-filing/analyze-document.ts
🧠 Learnings (36)
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must follow a standard structure: accept options with `inputData` and `emailAccount` parameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and use `createGenerateObject` to execute the LLM call
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/document-extraction.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/document-extraction.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts : LLM-related code must be organized in specific directories: `apps/web/utils/ai/` for main implementations, `apps/web/utils/llms/` for core utilities and configurations, and `apps/web/__tests__/` for LLM-specific tests
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-12-17T11:18:06.830Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.830Z
Learning: Applies to apps/web/**/utils/actions/*.validation.ts : Create separate Zod validation schema files for server action inputs
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Create separate validation files for server actions using the naming convention `apps/web/utils/actions/NAME.validation.ts` containing Zod schemas and inferred types
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `.schema()` method with Zod validation schemas from corresponding `.validation.ts` files in next-safe-action configuration
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : System prompts must define the LLM's role and task specifications
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `next-safe-action` with Zod schemas for all server actions (create/update/delete mutations), storing validation schemas in `apps/web/utils/actions/*.validation.ts`
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : All input parameters must be validated - check for presence, type, and format before use; use Zod schemas to validate request bodies with type guards and constraints
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*.validation.ts : Use descriptive error messages in Zod validation schemas
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*.validation.ts : Define validation schemas using Zod
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod validation schemas in separate `*.validation.ts` files and export both the schema and inferred type (e.g., `CreateExampleBody`)
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:53.147Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:53.147Z
Learning: Applies to **/*.validation.{ts,tsx} : Define validation schemas using Zod
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Applied to files:
apps/web/utils/ai/document-filing/analyze-document.tsapps/web/utils/drive/document-extraction.ts
📚 Learning: 2025-12-17T11:18:06.830Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.830Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/utils/drive/client.tsapps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/filebot/is-filebot-email.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Always validate that resources belong to the authenticated user before any operation - use ownership checks in queries (e.g., `emailAccount: { id: emailAccountId }`) and throw `SafeError` if validation fails
Applied to files:
apps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses
Applied to files:
apps/web/utils/filebot/is-filebot-email.tsapps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in updates - never directly assign request body to database update, always validate with schemas and only update specific fields
Applied to files:
apps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use Prisma relationships for access control by leveraging nested where clauses (e.g., `emailAccount: { id: emailAccountId }`) to validate ownership
Applied to files:
apps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. All `findUnique`/`findFirst` calls MUST include ownership filters
Applied to files:
apps/web/utils/drive/oauth-callback-helpers.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : ALL database queries MUST be scoped to the authenticated user/account - include user/account filtering in `where` clauses (e.g., `emailAccountId`, `userId`) to ensure users only access their own resources
Applied to files:
apps/web/utils/drive/oauth-callback-helpers.ts
🧬 Code graph analysis (4)
apps/web/utils/ai/document-filing/analyze-document.ts (4)
apps/web/utils/drive/types.ts (1)
DriveFolder(5-11)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-32)apps/web/utils/llms/index.ts (1)
createGenerateObject(139-217)apps/web/utils/drive/document-extraction.ts (1)
cleanExtractedText(233-239)
apps/web/utils/drive/client.ts (2)
apps/web/env.ts (1)
env(17-258)apps/web/utils/drive/scopes.ts (2)
GOOGLE_DRIVE_SCOPES(18-24)MICROSOFT_DRIVE_SCOPES(4-13)
apps/web/utils/filebot/is-filebot-email.ts (2)
apps/web/env.ts (1)
env(17-258)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)
apps/web/utils/drive/oauth-callback-helpers.ts (6)
apps/web/utils/calendar/oauth-callback-helpers.ts (1)
validateOAuthCallback(26-56)apps/web/utils/logger.ts (1)
Logger(5-5)apps/web/utils/drive/constants.ts (1)
DRIVE_STATE_COOKIE_NAME(1-1)apps/web/env.ts (1)
env(17-258)apps/web/utils/oauth/redirect.ts (1)
RedirectError(7-17)apps/web/utils/oauth/state.ts (1)
parseOAuthState(25-29)
🪛 ast-grep (0.40.0)
apps/web/utils/filebot/is-filebot-email.ts
[warning] 26-28: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${escapeRegex(localPart)}\\+${FILEBOT_PREFIX}-[a-zA-Z0-9]+@${escapeRegex(domain)}$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 69-71: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${escapeRegex(localPart)}\\+${FILEBOT_PREFIX}-([a-zA-Z0-9]+)@${escapeRegex(domain)}$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ 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: test
- GitHub Check: Review for correctness
🔇 Additional comments (19)
apps/web/utils/filebot/is-filebot-email.ts (6)
1-6: LGTM!The environment-dependent prefix pattern is well-designed, allowing clean separation between production and development filebot emails.
21-25: Validation implemented correctly.The validation for
localPartanddomainafter splitting, as well as the check forextractedEmailToCheck, properly addresses the concerns raised in previous reviews. The function now safely handles malformed email addresses.
27-30: ReDoS warning is likely a false positive.The static analysis tool flags potential ReDoS vulnerability, but the implementation is safe because:
- User-controlled parts (
localPartanddomain) are escaped viaescapeRegex- Pattern is anchored with
^and$- Token pattern
[a-zA-Z0-9]+is simple and linear- No nested quantifiers or catastrophic backtracking patterns
The current implementation properly mitigates ReDoS risks.
64-68: Validation implemented correctly.The validation checks for
localPart,domain, andextractedEmailToCheckproperly handle edge cases and address concerns from previous reviews.
70-74: ReDoS concern addressed by proper escaping.Similar to
isFilebotEmail, the static analysis warning is a false positive. TheescapeRegexfunction properly sanitizes user input, and the pattern structure prevents ReDoS attacks. The capture group correctly extracts the token portion.
77-79: LGTM!The
escapeRegexhelper correctly escapes all regex special characters, ensuring that user-controlled email parts cannot introduce regex injection or ReDoS vulnerabilities.apps/web/utils/drive/document-extraction.ts (6)
37-41: Past issue resolved:.docMIME type removed.The previous reviews flagged that
application/mswordwas listed but had no handler. This has been correctly addressed by removing it fromEXTRACTABLE_MIME_TYPES.
93-104: LGTM: Native PDF support detection is well-designed.The function correctly checks Claude's documented limits (32MB, 100 pages) and provides a clear API for determining when native PDF support can be used instead of extraction.
110-168: LGTM: PDF extraction logic is correct.The implementation correctly:
- Processes pages sequentially with configurable limits
- Tracks both character length and page count limits
- Handles truncation properly
- Logs useful debugging information
The type assertion on Line 134 is acceptable given unpdf's loose typing.
174-198: LGTM: DOCX extraction is straightforward and correct.The implementation properly uses mammoth's
extractRawTextAPI and handles truncation. Logging the mammoth messages is helpful for debugging.
233-239: LGTM: Text cleaning aligns with LLM best practices.The function correctly normalizes whitespace for LLM consumption, following the patterns from retrieved learnings about removing excessive whitespace.
Based on learnings about formatting data consistently for LLM functions.
180-182: Remove unnecessary@ts-expect-errorsuppression — mammoth v1.11.0 includes built-in TypeScript declarations.Mammoth added TypeScript declarations in version 1.4.19, and the project has mammoth 1.11.0 already installed. Since the package includes types out-of-the-box, the
@ts-expect-errorcomment is unnecessary. The comment suggesting to "pnpm add mammoth" is also misleading—the package is already a dependency. Simply import mammoth directly; TypeScript should resolve types without suppression.Likely an incorrect or invalid review comment.
apps/web/utils/drive/client.ts (4)
12-31: LGTM: Google OAuth configuration is correct.The implementation properly includes
prompt: "consent"andaccess_type: "offline"to ensure refresh tokens are issued.
36-65: LGTM: Google token exchange follows security best practices.The implementation correctly:
- Validates all required tokens are present
- Uses official Google ID token verification
- Validates audience claim
- Extracts user email from verified token
117-122: Past issue resolved: Error handling now checks response status first.The previous review flagged calling
response.json()before checkingresponse.ok. This has been correctly fixed - the code now checksresponse.okfirst and handles error bodies safely.
94-154: LGTM: Microsoft token exchange is well-implemented.The implementation correctly:
- Validates environment configuration
- Handles error responses safely
- Validates required tokens are present
- Fetches user profile to obtain email
- Provides email fallback (mail || userPrincipalName)
- Calculates token expiration time
apps/web/utils/drive/oauth-callback-helpers.ts (3)
32-36: LGTM: State validation schema is well-defined.The Zod schema properly validates the OAuth state with appropriate length constraints to prevent abuse.
76-101: LGTM: State parsing and validation is robust.The implementation correctly:
- Handles parse errors with try-catch
- Uses Zod for type-safe validation
- Logs detailed error information
- Throws appropriate redirect errors
147-171: Past issue resolved: Now uses atomic upsert.The previous reviews flagged a race condition with the
findFirst+create/updatepattern. This has been correctly fixed by using Prisma'supsertwith the compound unique constraintemailAccountId_provider, which provides atomic database-level guarantees.
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
3 issues found across 15 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx:305">
P1: Double event trigger: clicking the Checkbox fires both its `onCheckedChange` and the parent div's `onClick`, causing `handleFolderToggle` to be called twice and triggering duplicate API calls. Add `onClick={(e) => e.stopPropagation()}` to the Checkbox to prevent event bubbling.</violation>
</file>
<file name="apps/web/app/api/user/unsubscribe-one-click/route.ts">
<violation number="1" location="apps/web/app/api/user/unsubscribe-one-click/route.ts:69">
P1: Potential SSRF vulnerability: The `fetch` request is made to a URL extracted from email headers, which could be attacker-controlled. Consider validating that the URL is a public HTTP/HTTPS URL and not pointing to internal/private IP ranges (localhost, 10.x.x.x, 172.16.x.x, 192.168.x.x, 169.254.x.x, etc.) before making the request.</violation>
</file>
<file name="apps/web/utils/drive/handle-filing-reply.test.ts">
<violation number="1" location="apps/web/utils/drive/handle-filing-reply.test.ts:9">
P1: These tests define local copies of `parseFolderPath` and `isSkipCommand` rather than testing the actual production functions from `handle-filing-reply.ts`. This is a testing anti-pattern - if the production code changes, these tests won't detect regressions since they're testing their own inline implementations.
Consider either:
1. Export these functions (possibly via a separate `@internal` export) for testing
2. Test them indirectly through the public `handleFilingReply` function
3. Use a technique like `rewire` or test the module exports directly</violation>
</file>
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
apps/web/prisma/schema.prisma (1)
1002-1038: MakefolderIdandfolderPathnullable for PENDING status.As noted in the previous review, these fields are required (non-nullable) but documents with
PENDINGstatus (line 1167) will not have a determined destination folder yet. This will cause errors when creatingDocumentFilingrecords before the AI determines where to file the document.🔎 Proposed fix
// Result (after filing) - folderId String - folderPath String + folderId String? + folderPath String? fileId String? reasoning String? confidence Float?After updating the schema, run migration and regenerate the Prisma client:
#!/bin/bash npx prisma migrate dev --name make_filing_folders_optional npx prisma generateThen verify all TypeScript code that accesses these fields handles null values appropriately.
🧹 Nitpick comments (10)
apps/web/utils/drive/handle-filing-reply.test.ts (1)
3-27: Consider exporting helper functions for proper test coverage.The test file duplicates the implementation of
parseFolderPathandisSkipCommandinline rather than testing the actual functions fromhandle-filing-reply.ts. This means if the real implementation changes, these tests won't catch regressions.Consider either:
- Exporting the helper functions from
handle-filing-reply.tsfor direct testing- Creating an internal
handle-filing-reply.internal.tsmodule that exports these helpers- Testing through the public
processFilingReplyfunction with proper mockingapps/web/utils/drive/handle-filing-reply.ts (4)
126-138: Use Prisma enum instead of string literal for status.Per coding guidelines, import Prisma enums from
@/generated/prisma/enumsinstead of using string literals. This applies to"FILED"on line 131.🔎 Proposed fix
Add import at the top:
import { DocumentFilingStatus } from "@/generated/prisma/enums";Then update the status assignment:
data: { folderId: targetFolder.id, folderPath: folderPath, - status: "FILED", + status: DocumentFilingStatus.FILED, wasCorrected: filing.status === "FILED",
157-164: Use Prisma enum for ERROR status as well.🔎 Proposed fix
await prisma.documentFiling.update({ where: { id: filing.id }, - data: { status: "ERROR" }, + data: { status: DocumentFilingStatus.ERROR }, });
121-145: TODO: File moving and upload functionality is incomplete.The code has TODO comments for:
- Implementing file moving for corrections (Line 123)
- Fetching attachment and uploading to specified folder for pending filings (Line 143)
These represent incomplete functionality where the filing record is updated but the actual file operations don't occur.
Would you like me to help implement the file moving logic or open issues to track these tasks?
218-224: Use Prisma enum for REJECTED status.🔎 Proposed fix
async function handleSkip(filingId: string, logger: Logger): Promise<void> { await prisma.documentFiling.update({ where: { id: filingId }, - data: { status: "REJECTED" }, + data: { status: DocumentFilingStatus.REJECTED }, }); logger.info("Filing skipped by user"); }apps/web/app/api/user/drive/folders/route.ts (1)
57-78: Consider parallel folder fetching for better performance.The sequential
for...ofloop fetches folders from each drive one at a time. With multiple connected drives, this could be slow.🔎 Proposed optimization using Promise.all
- for (const connection of driveConnections) { - try { - const provider = createDriveProvider(connection, logger); - const folders = await provider.listFolders(undefined); - - for (const folder of folders) { - availableFolders.push({ - id: folder.id, - name: folder.name, - path: folder.name, - driveConnectionId: connection.id, - provider: connection.provider, - }); - } - } catch (error) { - logger.warn("Error fetching folders from drive", { - connectionId: connection.id, - provider: connection.provider, - error, - }); - } - } + const folderResults = await Promise.allSettled( + driveConnections.map(async (connection) => { + const provider = createDriveProvider(connection, logger); + const folders = await provider.listFolders(undefined); + return folders.map((folder) => ({ + id: folder.id, + name: folder.name, + path: folder.name, + driveConnectionId: connection.id, + provider: connection.provider, + })); + }), + ); + + for (const [index, result] of folderResults.entries()) { + if (result.status === "fulfilled") { + availableFolders.push(...result.value); + } else { + const connection = driveConnections[index]; + logger.warn("Error fetching folders from drive", { + connectionId: connection.id, + provider: connection.provider, + error: result.reason, + }); + } + }apps/web/utils/actions/drive.ts (1)
101-112: Consider including ownership filter in the query.The current pattern queries by
idthen validates ownership separately. While functionally safe, the project guidelines prefer including ownership filters directly in WHERE clauses.🔎 Proposed improvement
export const removeFilingFolderAction = actionClient .metadata({ name: "removeFilingFolder" }) .inputSchema(removeFilingFolderBody) .action(async ({ ctx: { emailAccountId }, parsedInput: { id } }) => { const folder = await prisma.filingFolder.findUnique({ - where: { id }, + where: { id, emailAccountId }, }); - if (!folder || folder.emailAccountId !== emailAccountId) { + if (!folder) { throw new SafeError("Filing folder not found"); } await prisma.filingFolder.delete({ where: { id }, }); });apps/web/app/api/user/unsubscribe-one-click/route.ts (1)
27-28: Consider wrapping Zod parse in try/catch for better error responses.If the request body fails Zod validation,
.parse()throws aZodErrorwhich may not be handled gracefully by the middleware, potentially exposing validation details.🔎 Proposed safer parsing
const body = await request.json(); - const { senderEmail } = unsubscribeSchema.parse(body); + const parsed = unsubscribeSchema.safeParse(body); + if (!parsed.success) { + return NextResponse.json( + { success: false, method: "fallback", error: "Invalid request" } satisfies OneClickUnsubscribeResponse, + { status: 400 }, + ); + } + const { senderEmail } = parsed.data;apps/web/utils/drive/filing-engine.ts (1)
183-184: Consider extracting confidence threshold as a constant.The
0.7threshold is a magic number that determines whether to ask the user or file automatically. Consider extracting this to a named constant for clarity and easier adjustment.🔎 Proposed improvement
+const CONFIDENCE_THRESHOLD = 0.7; + // Step 7: Determine if we should ask the user first -const shouldAsk = analysis.confidence < 0.7; +const shouldAsk = analysis.confidence < CONFIDENCE_THRESHOLD;apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx (1)
179-193: Consider wrappinghandleFolderToggleinuseCallback.While not critical for correctness, wrapping this handler in
useCallbackwould follow React best practices and prevent unnecessary re-creation on every render.🔎 Proposed refactor
+ const handleFolderToggle = useCallback((folder: FolderItem, isChecked: boolean) => { - const handleFolderToggle = (folder: FolderItem, isChecked: boolean) => { if (isChecked) { addFolder({ folderId: folder.id, folderName: folder.name, folderPath: folder.path, driveConnectionId: folder.driveConnectionId, }); } else { const saved = savedFolders.find((f) => f.folderId === folder.id); if (saved) { removeFolder({ id: saved.id }); } } - }; + }, [addFolder, removeFolder, savedFolders]);
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
Outdated
Show resolved
Hide resolved
| if (result?.serverError) { | ||
| toastError({ | ||
| title: "Error saving rules", | ||
| description: result.serverError, | ||
| }); | ||
| } else { | ||
| mutateEmail(); | ||
| // Trigger preview after successful save | ||
| onPreviewClick(); | ||
| } |
There was a problem hiding this comment.
onSubmit ignores result?.validationErrors, so preview proceeds after a failed server-side validation. Consider handling validationErrors and returning early, similar to serverError.
| if (result?.serverError) { | |
| toastError({ | |
| title: "Error saving rules", | |
| description: result.serverError, | |
| }); | |
| } else { | |
| mutateEmail(); | |
| // Trigger preview after successful save | |
| onPreviewClick(); | |
| } | |
| if (result?.serverError || result?.validationErrors) { | |
| toastError({ | |
| title: "Error saving rules", | |
| description: result.serverError || "Validation failed. Please check your input.", | |
| }); | |
| return; | |
| } else { | |
| mutateEmail(); | |
| // Trigger preview after successful save | |
| onPreviewClick(); | |
| } |
🚀 Want me to fix this? Reply ex: "fix it for me".
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx:110">
P2: Missing `.catch()` handler on the promise chain. If `fileAttachmentAction` throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at "filing" status.</violation>
</file>
Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.
| fileAttachmentAction(emailAccountId, { | ||
| messageId: att.messageId, | ||
| filename: att.filename, | ||
| }).then((result) => { |
There was a problem hiding this comment.
P2: Missing .catch() handler on the promise chain. If fileAttachmentAction throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at "filing" status.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx, line 110:
<comment>Missing `.catch()` handler on the promise chain. If `fileAttachmentAction` throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at "filing" status.</comment>
<file context>
@@ -82,11 +89,54 @@ export function DriveSetup() {
+ fileAttachmentAction(emailAccountId, {
+ messageId: att.messageId,
+ filename: att.filename,
+ }).then((result) => {
+ const resultData = result?.data;
+ if (result?.serverError) {
</file context>
| userPhase === "previewing" || userPhase === "starting"; | ||
| const { data: attachmentsData, isLoading: attachmentsLoading } = | ||
| useFilingPreviewAttachments(shouldFetchAttachments, { | ||
| onSuccess: (data) => { |
There was a problem hiding this comment.
Race condition: setFilingStates(initial) runs after starting fileAttachmentAction promises and can overwrite resolved updates. Consider setting the initial state before firing the promises.
🚀 Want me to fix this? Reply ex: "fix it for me".
| .filter("folder ne null") // Only get folders, not files | ||
| .select("id,name,parentReference,webUrl") | ||
| .top(200) // Increase limit for better visibility | ||
| .get(); | ||
|
|
||
| const items: DriveItem[] = response.value || []; | ||
|
|
||
| return items.map((item) => this.convertToFolder(item)); |
There was a problem hiding this comment.
$filter=folder ne null likely doesn’t work on Graph DriveItem facets and may return files. Consider removing the server-side filter and filter client-side with item.folder before mapping so only folders are returned.
- .filter("folder ne null") // Only get folders, not files
- return items.map((item) => this.convertToFolder(item));
+ return items.filter((item) => item.folder).map((item) => this.convertToFolder(item));🚀 Want me to fix this? Reply ex: "fix it for me".
| const replyToAddress = getFilebotEmail({ userEmail }); | ||
|
|
||
| const subject = `✓ Filed ${filing.filename}`; | ||
| const messageHtml = buildFiledEmailHtml({ |
There was a problem hiding this comment.
filing.driveConnection.provider may throw if driveConnection is null. Consider guarding for a missing relation and log/return before building the email.
| const messageHtml = buildFiledEmailHtml({ | |
| if (!filing.driveConnection) { | |
| log.error("Filing missing driveConnection"); | |
| return; | |
| } | |
| const messageHtml = buildFiledEmailHtml({ |
🚀 Want me to fix this? Reply ex: "fix it for me".
There was a problem hiding this comment.
1 issue found across 9 files (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx">
<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx:83">
P2: Unsafe type assertion: `connection?.provider` can be `undefined` when no connection is found, but `as DriveProviderType` assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.</violation>
</file>
Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.
| const connection = connections.find((c) => c.id === filing.driveConnectionId); | ||
|
|
||
| const driveUrl = filing.fileId | ||
| ? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType) |
There was a problem hiding this comment.
P2: Unsafe type assertion: connection?.provider can be undefined when no connection is found, but as DriveProviderType assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx, line 83:
<comment>Unsafe type assertion: `connection?.provider` can be `undefined` when no connection is found, but `as DriveProviderType` assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.</comment>
<file context>
@@ -79,7 +80,7 @@ function FilingRow({
const driveUrl = filing.fileId
- ? getDriveFileUrl(filing.fileId, connection?.provider || "")
+ ? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType)
: null;
</file context>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (18)
apps/web/components/kibo-ui/tree/index.tsx (3)
190-190: UnusedonClickprop.The
onClickprop is destructured but never used. Per past review comments, either pass it to the<div>at line 217 or remove it from destructuring to let it flow via...props.
306-306: Width calculation can produce negative values.Per past review comments, when
indentis less than 4, thewidth: (indent ?? 0) - 4calculation produces a negative value, which may render oddly. Consider clamping to 0.🔎 Suggested fix
- width: (indent ?? 0) - 4, + width: Math.max(0, (indent ?? 0) - 4),
86-95: Semi-controlled state can cause stale UI.This issue was previously flagged: when
selectedIdsis provided withoutonSelectionChange,internalSelectedIdsis initialized once but never syncs with subsequentselectedIdsprop updates, leading to stale selection state.Based on past review comments, consider either:
- Treating
selectedIdsas controlled whenever provided (read-only mode is valid)- Adding a
useEffectto syncinternalSelectedIdswhenselectedIdschangesapps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx (1)
82-84: Guard provider before callinggetDriveFileUrlto prevent runtime errors.The type assertion
as DriveProviderTypeat line 83 is unsafe. Whenconnectionis undefined (e.g., if the matching connection hasn't loaded yet or doesn't exist),connection?.providerevaluates toundefined, but the type assertion tricks TypeScript into thinking it's a validDriveProviderType. This causesgetDriveFileUrlto hit its default case, log an exception for "Invalid provider", and return an invalid value.🔎 Recommended fix
- const driveUrl = filing.fileId - ? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType) + const driveUrl = filing.fileId && connection?.provider + ? getDriveFileUrl(filing.fileId, connection.provider) : null;Based on past review comments from cubic-dev-ai[bot] and macroscopeapp[bot].
apps/web/app/(app)/[emailAccountId]/drive/AllowedFolders.tsx (5)
263-263: TreeExpander hasChildren is hardcoded.Hardcoding
hasChildren={true}always shows the expand chevron, even for folders that truly have no children. This is a duplicate of an earlier review comment.
106-106: Action calls still use incorrect signature.Both
addFilingFolderActionandremoveFilingFolderActionare called withemailAccountIdas the first parameter, butactionClientautomatically providesemailAccountIdvia context. Per the established pattern, these should be called with only the input object.Based on learnings,
actionClientprovidesemailAccountIdviactxautomatically.Also applies to: 122-122
289-292: Missing driveConnectionId inheritance in recursive calls.When constructing child
folderobjects for recursion,subfolder.driveConnectionIdmay be undefined. The childFolderNodethen callsuseDriveSubfolderswithundefined, causing the hook to fail. Consider inheriting from the parent as suggested in the earlier review.
299-299: knownChildren not threaded through recursion.Recursive
FolderNodecalls at line 287 don't receiveknownChildren, so deeper levels always refetch from the server instead of using cached data fromfolderChildrenMap. This impacts performance with unnecessary requests. This is a duplicate of an earlier comment.
377-377: Dialog onOpenChange handler ignores boolean parameter.
onOpenChangereceives a boolean (open) butonToggledoesn't accept it, so closing via ESC or backdrop click will toggle the dialog back open. This is a duplicate of an earlier review comment.apps/web/utils/drive/document-extraction.ts (2)
128-153: Truncation logic doesn't account for separators (known issue).The current implementation tracks
totalLengthbased on individual page lengths but doesn't account for the"\n\n"separators added duringjoin()at line 153. This can cause the final text to exceedmaxLengthby up to(pageCount - 1) * 2characters.As noted in previous reviews, consider incorporating separator length into the truncation check:
Suggested adjustment
for (let i = 1; i <= pagesToProcess && !truncated; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); const pageText = (textContent.items as Array<{ str?: string }>) .map((item) => item.str ?? "") .join(" "); + const sepLen = textParts.length > 0 ? 2 : 0; - if (totalLength + pageText.length > maxLength) { + if (totalLength + sepLen + pageText.length > maxLength) { - const remaining = maxLength - totalLength; + const remaining = maxLength - totalLength - sepLen; - textParts.push(pageText.slice(0, remaining)); + if (remaining > 0) textParts.push(pageText.slice(0, remaining)); truncated = true; } else { textParts.push(pageText); - totalLength += pageText.length; + totalLength += sepLen + pageText.length; } }
177-199: DOCX extraction improved but memory concern remains.Good improvement: logging now uses
messageCountinstead of exposing potentially sensitivemessagescontent.The full-text extraction before truncation (line 185) is still a potential memory concern for very large DOCX files, as noted in previous reviews. This appears to be a limitation of mammoth's API, which doesn't offer streaming. Consider documenting this limitation in a comment or adding a buffer-size pre-check if memory issues arise in production.
apps/web/utils/drive/handle-filing-reply.ts (4)
201-222: Improve folder path parsing robustness.Two issues limit the parser's effectiveness:
Line 208: The filter
!trimmed.startsWith("On ")is too broad and would skip valid folder paths like"On Hold/2024"or"Ongoing Projects". Consider using a more specific pattern like/^On .+wrote:$/ito match only email reply headers.Line 210-213: The cleaning logic only removes prefixes at the start of the line. A reply like
"Thanks! Put it in Receipts/2024"would return the full sentence instead of extracting just"Receipts/2024". Consider searching for the command phrase anywhere in the line and extracting only the path portion.These were noted in previous reviews.
🔎 Suggested improvements
function parseFolderPath(content: string): string | null { const lines = content.split("\n"); for (const line of lines) { const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith(">") && !trimmed.startsWith("On ")) { + // Skip quoted lines and typical reply headers + if (trimmed && !trimmed.startsWith(">") && !/^On .+wrote:$/i.test(trimmed)) { - // Remove common prefixes - const cleaned = trimmed - .replace(/^(put it in|move to|file to|folder:?)\s*/i, "") + // Look for command phrases and extract the path after them + const commandMatch = trimmed.match(/(?:put it in|move to|file to|folder:?)\s*(.+?)$/i); + const pathCandidate = commandMatch ? commandMatch[1] : trimmed; + + const cleaned = pathCandidate .replace(/^["']|["']$/g, "") .trim(); if (cleaned) { return cleaned; } } } return null; }
106-119: Critical: Add null check forfiling.driveConnectionbefore use.The
filing.driveConnectionfrom the Prisma include (line 65) can be null. Passing it tocreateDriveProviderWithRefreshat line 108 will cause a runtime error when the function attempts to destructure the connection object at line 59 of provider.ts. This was flagged in previous reviews.🔎 Proposed fix
try { + if (!filing.driveConnection) { + logger.error("Filing has no drive connection"); + await prisma.documentFiling.update({ + where: { id: filing.id }, + data: { status: "ERROR" }, + }); + return; + } + const driveProvider = await createDriveProviderWithRefresh( filing.driveConnection, logger, );
147-152: Consider handling missing message-id more gracefully.Using an empty string for
headerMessageIdwhenmessage.headers["message-id"]is missing can break reply threading in the confirmation email. Consider either making the field optional in the confirmation flow or logging a warning when the header is absent. This was noted in previous reviews.🔎 Suggested improvement
const sourceMessage = { threadId: message.threadId, - headerMessageId: message.headers["message-id"] || "", + headerMessageId: message.headers["message-id"], references: message.headers.references, }; + + if (!sourceMessage.headerMessageId) { + logger.warn("Message-ID header missing, reply threading may not work correctly"); + }Note: This requires updating the
sendCorrectionConfirmationfunction signature to acceptheaderMessageId?: stringinstead of requiring it.
121-145: Status set toFILEDbefore file operation completes.The filing status is updated to
"FILED"at line 131, but the file moving (line 123 TODO) and upload (line 143 TODO) operations are not implemented. This creates inconsistent database state where a filing is marked as successfully filed but the file hasn't been moved or uploaded.Consider using an intermediate status (e.g.,
"PENDING_MOVE") until the file operation is implemented, or defer the status update until the operation completes. This was flagged in previous reviews.🔎 Suggested approach
await prisma.documentFiling.update({ where: { id: filing.id }, data: { folderId: targetFolder.id, folderPath: folderPath, - status: "FILED", + status: filing.fileId ? "PENDING_MOVE" : "PENDING", wasCorrected: filing.status === "FILED", originalPath: filing.wasCorrected ? filing.originalPath : filing.folderPath, correctedAt: new Date(), }, });Then update to
"FILED"only after the move/upload operation succeeds.apps/web/utils/drive/provider.ts (3)
45-45: Use SafeError for user-facing error message.This error can surface to users when an unsupported provider type is encountered. The same issue exists at line 85 and was flagged in previous reviews.
🔎 Proposed fix
- throw new Error(`Unsupported drive provider: ${provider}`); + throw new SafeError( + "Unable to access your drive. Please reconnect your drive and try again.", + );Based on learnings, use SafeError for error responses to prevent information disclosure.
295-300: Critical: Add user scoping to database update to prevent IDOR vulnerability.The Prisma update uses only
connectionIdin the WHERE clause without validating ownership. Per coding guidelines, ALL database queries must be scoped to the authenticated user/account. This is the same issue flagged in previous reviews.Update the function signature to accept
emailAccountIdand include it in the WHERE clause:where: { id: connectionId, emailAccountId }. Update all call sites at lines 129, 141, 151, 211, 223, and 233 to pass theemailAccountIdfrom the connection object.🔎 Proposed fix
-async function markDriveConnectionAsDisconnected(connectionId: string) { +async function markDriveConnectionAsDisconnected( + connectionId: string, + emailAccountId: string, +) { await prisma.driveConnection.update({ - where: { id: connectionId }, + where: { id: connectionId, emailAccountId }, data: { isConnected: false }, }); }Then update call sites to pass
emailAccountId, for example at line 129:- await markDriveConnectionAsDisconnected(connectionId); + await markDriveConnectionAsDisconnected(connectionId, connection.emailAccountId);Note: The
refreshMicrosoftDriveTokenandrefreshGoogleDriveTokenfunction signatures will need to be updated to includeemailAccountIdin the connection pick type.Based on learnings, ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses.
255-293: Critical: Add user scoping to database update to prevent IDOR vulnerability.The Prisma update at lines 276-286 uses only
connectionIdin the WHERE clause without validating ownership. Per coding guidelines, ALL database queries must be scoped to the authenticated user/account to prevent Insecure Direct Object References.Update the function signature to accept
emailAccountIdand include it in the WHERE clause:where: { id: connectionId, emailAccountId }. Update all call sites at lines 159 and 241 to pass theemailAccountIdfrom the connection object.🔎 Proposed fix
async function saveDriveTokens({ tokens, connectionId, + emailAccountId, logger, }: { tokens: { access_token?: string; refresh_token?: string; expires_at?: number; }; connectionId: string; + emailAccountId: string; logger: Logger; }) { if (!tokens.access_token) { logger.warn("No access token to save for drive connection", { connectionId, }); return; } try { await prisma.driveConnection.update({ - where: { id: connectionId }, + where: { id: connectionId, emailAccountId }, data: { accessToken: tokens.access_token, ...(tokens.refresh_token && { refreshToken: tokens.refresh_token }), expiresAt: tokens.expires_at ? new Date(tokens.expires_at * 1000) : null, isConnected: true, }, });Then update the call sites at lines 159 and 241:
await saveDriveTokens({ tokens: { access_token: tokens.access_token, refresh_token: tokens.refresh_token, expires_at: Number.isFinite(expiresIn) ? Math.floor(Date.now() / 1000 + expiresIn) : undefined, }, connectionId, + emailAccountId: connection.emailAccountId, logger, });Based on learnings, ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses.
🧹 Nitpick comments (3)
apps/web/components/kibo-ui/tree/index.tsx (1)
267-267: Remove unnecessary type cast.The
as ReactNodecast is unnecessary sinceComponentProps<typeof motion.div>already includes proper typing for children.🔎 Suggested simplification
- {children as ReactNode} + {children}apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx (1)
29-29: Consider handling errors from drive connections fetch.While the component gracefully degrades when
connectionsDatais undefined (using an empty array fallback at line 56), connection fetch errors are silently ignored. Consider displaying a warning or falling back more explicitly when drive connections fail to load but filings succeed.🔎 Example implementation
export function FilingActivity() { const { data, isLoading, error } = useFilingActivity({ limit: 10, offset: 0, }); - const { data: connectionsData } = useDriveConnections(); + const { data: connectionsData, error: connectionsError } = useDriveConnections(); return ( <div> <SectionHeader className="mb-3">Recent Activity</SectionHeader> + {connectionsError && ( + <div className="mb-2 text-sm text-amber-600 dark:text-amber-400"> + Unable to load drive connections. External links may not be available. + </div> + )} <LoadingContent loading={isLoading} error={error}>apps/web/utils/drive/handle-filing-reply.ts (1)
179-188: Remove unnecessary optional chaining.The
extractEmailAddressfunction returnsstring(empty string for invalid input), not a nullable type. The optional chaining at line 186 is unnecessary. However, consider adding a guard for whenextractEmailAddressreturns an empty string to make the intent clearer.🔎 Proposed refactor
function verifyUserSentEmail({ message, userEmail, }: { message: ParsedMessage; userEmail: string; }): boolean { - const fromEmail = extractEmailAddress(message.headers.from)?.toLowerCase(); - return fromEmail === userEmail.toLowerCase(); + const fromEmail = extractEmailAddress(message.headers.from).toLowerCase(); + if (!fromEmail) { + return false; + } + return fromEmail === userEmail.toLowerCase(); }
| const result = await createDriveFolderAction(emailAccountId, { | ||
| ...data, | ||
| driveConnectionId, | ||
| }); |
There was a problem hiding this comment.
Action call uses incorrect signature.
createDriveFolderAction is called with emailAccountId as the first parameter. Like the other action calls in this file, actionClient should provide emailAccountId via context automatically. The call should be createDriveFolderAction({ ...data, driveConnectionId }) without the emailAccountId parameter.
Based on learnings, actionClient provides emailAccountId via ctx automatically.
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/drive/AllowedFolders.tsx around lines
345-348, the call to createDriveFolderAction incorrectly passes emailAccountId
as the first positional argument; remove that first argument and call
createDriveFolderAction with a single payload object (createDriveFolderAction({
...data, driveConnectionId })) so the actionClient can supply emailAccountId via
ctx automatically; update the call accordingly and ensure no other code relies
on the positional parameter.
| if (!accessToken) { | ||
| throw new Error("No access token available for drive connection"); | ||
| } |
There was a problem hiding this comment.
Use SafeError for user-facing error message.
This error can surface to users through createDriveProviderWithRefresh. Use SafeError to prevent information disclosure, consistent with the error handling at lines 62-64 and throughout the file.
🔎 Proposed fix
if (!accessToken) {
- throw new Error("No access token available for drive connection");
+ throw new SafeError(
+ "Unable to access your drive. Please reconnect your drive and try again.",
+ );
}Based on learnings, use SafeError for error responses to prevent information disclosure.
🤖 Prompt for AI Agents
In apps/web/utils/drive/provider.ts around lines 33 to 35, the code throws a
plain Error when no access token is available — replace that with throwing a
SafeError to avoid leaking internal details to users; update the throw to `throw
new SafeError("No access token available for drive connection")` and add the
appropriate import for SafeError from the project's error utilities (matching
other uses in this file, e.g., lines 62-64) so the file compiles and remains
consistent.
There was a problem hiding this comment.
This PR is being reviewed by Cursor Bugbot
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| hasFolders={foldersData ? foldersData.savedFolders.length > 0 : false} | ||
| phase={displayPhase} | ||
| onPreviewClick={handlePreviewClick} | ||
| /> |
There was a problem hiding this comment.
Form shows empty value instead of saved filing prompt
The SetupRulesForm component is rendered immediately with initialPrompt={emailAccount?.filingPrompt || ""}. Since emailAccount data is fetched asynchronously via useEmailAccountFull(), on first render it's undefined, causing initialPrompt to be an empty string. The form's defaultValues in react-hook-form are only applied once at mount, so when the data loads, the form doesn't update to show the saved prompt. Compare this to FilingRulesForm which correctly wraps the form in LoadingContent to wait for data. Users returning to setup will see an empty form instead of their saved filing rules.
Additional Locations (1)
| [key]: { status: "error", error: "Unknown error" }, | ||
| })); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Missing error handler causes preview flow to get stuck
The fileAttachmentAction calls inside the onSuccess callback use .then() without a .catch() handler. If a network error or unexpected exception causes the promise to reject, the error is unhandled and the attachment's state remains permanently stuck at "filing" status. Since anyFiling checks if any attachment is still in "filing" status, the "Looks good, start auto-filing" button stays disabled indefinitely, preventing users from completing the setup flow.
|
This change request has over 100 files and exceeds the review limit. Consider splitting it into smaller CRs for faster and more effective reviews |
|
@elie222 I love this feature, this is going to be awesome! |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (1)
37-52: Consider moving helper function to the bottom of the file.The
getProviderInfohelper function is currently defined before the main component. Per coding guidelines, helper functions should be added to the bottom of files for consistency.📝 Suggested refactor
Move the
getProviderInfofunction to after the component definition (after line 213).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
README.mdapps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(landing)/components/page.tsxapps/web/components/SideNav.tsxapps/web/package.jsonapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/types.tspackage.json
✅ Files skipped from review due to trivial changes (1)
- apps/web/utils/email/reply-all.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/web/utils/email/google.ts
- apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
- apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
- apps/web/package.json
- apps/web/utils/email/microsoft.ts
🧰 Additional context used
📓 Path-based instructions (26)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests
Files:
apps/web/utils/email/reply-all.test.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tspackage.jsonapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxREADME.mdapps/web/app/(landing)/components/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.tsapps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/email/reply-all.test.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/notes.mdc)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/email/reply-all.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests
Files:
apps/web/utils/email/reply-all.test.ts
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/installing-packages.mdc)
Use
pnpmas the package manager
Files:
package.json
apps/web/components/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Use
LoadingContentcomponent to consistently handle loading and error states, passingloading,error, andchildrenpropsUse PascalCase for component file names (e.g.,
components/Button.tsx)
Files:
apps/web/components/SideNav.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/components/SideNav.tsxapps/web/app/(landing)/components/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/components/SideNav.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
*.md
📄 CodeRabbit inference engine (.cursor/rules/task-list.mdc)
*.md: Create task lists in markdown files namedTASKS.mdor with a descriptive feature-specific name (e.g.,ASSISTANT_CHAT.md) in the project root to track project progress
Structure task list markdown files with sections: Feature Name Implementation (title), description, Completed Tasks, In Progress Tasks, Future Tasks, Implementation Plan, and Relevant Files subsections
Update task list markdown files by marking tasks as completed with[x], adding new identified tasks, and moving tasks between Completed/In Progress/Future sections as appropriate
Keep the 'Relevant Files' section in task list markdown files updated with file paths that have been created or modified, brief descriptions of each file's purpose, and status indicators (e.g., ✅) for completed components
Files:
README.md
🧠 Learnings (41)
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.test.{ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/email/types.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.test.tsapps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts : LLM-related code must be organized in specific directories: `apps/web/utils/ai/` for main implementations, `apps/web/utils/llms/` for core utilities and configurations, and `apps/web/__tests__/` for LLM-specific tests
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task
Applied to files:
package.json
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependencies
Applied to files:
package.json
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Use Turborepo with pnpm workspaces for project dependency management
Applied to files:
package.json
📚 Learning: 2025-11-25T14:37:45.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/installing-packages.mdc:0-0
Timestamp: 2025-11-25T14:37:45.660Z
Learning: Applies to **/package.json : Use `pnpm` as the package manager
Applied to files:
package.json
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/components/SideNav.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) to wrap `useSWR` for individual API endpoints
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : For early access features, create boolean flag hooks using `useFeatureFlagEnabled` with the pattern `export function use[FeatureName]Enabled()`
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : For early access feature flags, create hooks using the naming convention `use[FeatureName]Enabled` that return a boolean from `useFeatureFlagEnabled("flag-key")`
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use `use[FeatureName]Enabled` naming convention for boolean feature flag hooks and `use[FeatureName]Variant` for A/B test variant hooks
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Use the `use` prefix for custom hook filenames (e.g., `useAccounts.ts`)
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Custom hooks must use the `use` prefix in their filename (e.g., `useAccounts.ts`)
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/components/SideNav.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use the `LoadingContent` component to handle loading states
Applied to files:
apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use `next/image` package for images
Applied to files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `next/image` package for images
Applied to files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive
Applied to files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{jsx,tsx} : Don't use `<img>` elements in Next.js projects
Applied to files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2024-08-23T11:37:26.779Z
Learnt from: aryanprince
Repo: elie222/inbox-zero PR: 210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.
Applied to files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use shadcn/ui components when available
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use Shadcn UI and Tailwind for components and styling
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use Shadcn UI and Tailwind for components and styling
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Implement responsive design with Tailwind CSS using a mobile-first approach
Applied to files:
apps/web/app/(landing)/components/page.tsx
🧬 Code graph analysis (2)
apps/web/utils/email/types.ts (1)
apps/web/utils/types.ts (1)
ParsedMessage(51-73)
apps/web/components/SideNav.tsx (1)
apps/web/hooks/useFeatureFlags.ts (1)
useSmartFilingEnabled(20-23)
⏰ 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: test
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
README.md (3)
46-47: Feature list entries are clear and follow existing patterns.The additions to the Features section correctly document the two new capabilities (Meeting Briefs and Smart Filing) with appropriate beta/early-access labels that set proper user expectations.
162-162: Google Drive integration setup documentation is complete and consistent.The callback URI, scope, and API enablement follow the established patterns used for Google Calendar integration. The conditional notes ("only required for Google Drive integration") align with the documentation style.
Also applies to: 181-181, 195-195
242-242: OneDrive integration setup documentation matches Microsoft OAuth pattern.The callback URI and Files.ReadWrite permission follow the structure of existing Microsoft Graph integration documentation and are consistent with OneDrive's requirements.
Also applies to: 272-272
apps/web/utils/email/reply-all.test.ts (1)
2-6: LGTM! Formatting improvement.The import statement has been reformatted to multiline with trailing commas, which improves readability and makes future diffs cleaner when adding or removing imports. This is a Prettier-driven formatting change with no functional impact.
apps/web/utils/email/types.ts (1)
184-190: LGTM! Method signature follows interface conventions.The new
getMessagesWithAttachmentsmethod is well-designed and consistent with existing pagination methods in the interface. The signature is properly typed, uses optional parameters appropriately, and includes pagination support viapageToken.apps/web/components/SideNav.tsx (1)
19-19: LGTM! Clean feature flag integration.The Smart Filing navigation item is correctly gated by the
useSmartFilingEnabled()hook, following the established pattern for feature-flagged items. The implementation is consistent with existing conditional navigation items (Integrations, Meeting Briefs) and properly updates the memoization dependencies.Key strengths:
- Feature flag hook follows the
use[FeatureName]Enablednaming convention- Correct React hooks usage (top-level, proper dependencies)
- Uses
prefixPathhelper consistently- Marked as
beta: true, appropriate for early-access features- Type-safe and accessible
Also applies to: 56-56, 83-83, 132-141, 153-159
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (1)
3-3: LGTM! Import deduplication improves code organization.Moving the
Imageimport to the top of the file follows best practices and the coding guideline that all imports should be at the top of files.apps/web/app/(landing)/components/page.tsx (2)
4-12: LGTM: Expanded imports support the new component demonstrations.The additional Card subcomponents (CardHeader, CardContent, CardFooter, CardTitle, CardDescription) and Typography components (PageSubHeading, MutedText) are properly imported to support the enhanced component showcase sections below.
Also applies to: 16-16, 24-24
76-76: LGTM: Consistent use of MutedText component.The replacement of inline muted text styling with the MutedText component improves consistency and maintainability across the component showcase. This follows good component composition practices.
Also applies to: 80-80, 264-266, 273-273, 280-280, 422-424, 614-614, 620-620, 627-627, 648-648, 727-727, 739-739, 751-751, 765-767, 782-784
package.json (1)
25-25: Update @turbo/gen to match turbo version for monorepo compatibility.The current versions are misaligned:
turbo@2.7.1is paired with@turbo/gen@2.6.2. Per Turborepo's documentation, @turbo/gen versions should be kept aligned with turbo in the monorepo root to avoid TypeScript type mismatches and tooling surprises. Update @turbo/gen to2.7.1to match (or2.7.2which is now the latest for both packages).⛔ Skipped due to learnings
Learnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/project-structure.mdc:0-0 Timestamp: 2025-11-25T14:38:56.992Z Learning: Use Turborepo with pnpm workspaces for project dependency managementLearnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/installing-packages.mdc:0-0 Timestamp: 2025-11-25T14:37:45.660Z Learning: Applies to **/package.json : Use `pnpm` as the package managerLearnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/environment-variables.mdc:0-0 Timestamp: 2025-11-25T14:36:43.454Z Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependenciesLearnt from: CR Repo: elie222/inbox-zero PR: 0 File: .cursor/rules/environment-variables.mdc:0-0 Timestamp: 2025-11-25T14:36:45.807Z Learning: Applies to turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task
| if (result.success && result.filing) { | ||
| filings.push({ | ||
| filingId: result.filing.id, | ||
| filename: result.filing.filename, | ||
| folderPath: result.filing.folderPath, | ||
| fileId: result.filing.fileId, | ||
| filedAt: new Date().toISOString(), | ||
| provider: result.filing.provider as DriveProviderType, | ||
| }); |
There was a problem hiding this comment.
result.filing.provider as DriveProviderType is unsafe. It can pass unexpected providers to clients. Consider guarding the value at runtime and skip (or handle) unknown providers before pushing the result.
| if (result.success && result.filing) { | |
| filings.push({ | |
| filingId: result.filing.id, | |
| filename: result.filing.filename, | |
| folderPath: result.filing.folderPath, | |
| fileId: result.filing.fileId, | |
| filedAt: new Date().toISOString(), | |
| provider: result.filing.provider as DriveProviderType, | |
| }); | |
| if (result.success && result.filing) { | |
| if (result.filing.provider !== "google" && result.filing.provider !== "microsoft") { | |
| logger.warn("Unknown drive provider in filing preview", { provider: result.filing.provider }); | |
| continue; | |
| } | |
| filings.push({ | |
| filingId: result.filing.id, | |
| filename: result.filing.filename, | |
| folderPath: result.filing.folderPath, | |
| fileId: result.filing.fileId, | |
| filedAt: new Date().toISOString(), | |
| provider: result.filing.provider as DriveProviderType, | |
| }); |
🚀 Want me to fix this? Reply ex: "fix it for me".
|
|
||
| return { | ||
| filings, | ||
| noAttachmentsFound: filings.length === 0, |
There was a problem hiding this comment.
noAttachmentsFound uses filings.length === 0, so it can report no attachments even when attachments existed but filing failed. Consider using messagesWithAttachments.length === 0 to only signal when none were found.
| noAttachmentsFound: filings.length === 0, | |
| noAttachmentsFound: messagesWithAttachments.length === 0, |
🚀 Want me to fix this? Reply ex: "fix it for me".
| connectionId, | ||
| status: response.status, | ||
| }); | ||
| await markDriveConnectionAsDisconnected(connectionId); |
There was a problem hiding this comment.
Suggestion: markDriveConnectionAsDisconnected(...) can throw and hide the original SafeError. Wrap calls in try/catch and log failures so the disconnect error never replaces the root cause; always propagate the original user-facing error.
| await markDriveConnectionAsDisconnected(connectionId); | |
| try { | |
| await markDriveConnectionAsDisconnected(connectionId); | |
| } catch (err) { | |
| logger.error("Failed to mark drive connection as disconnected", { | |
| connectionId, | |
| error: err, | |
| }); | |
| } |
🚀 Want me to fix this? Reply ex: "fix it for me".
| id: item.id ?? "", | ||
| name, | ||
| parentId: item.parentReference?.id ?? undefined, | ||
| path: item.parentReference?.path |
There was a problem hiding this comment.
convertToFolder builds path from item.parentReference.path, which uses Graph’s internal format (e.g., /drive/root:/...). This can look wrong for display. Consider normalizing to a human-readable path (strip the /drive/root: prefix and colon semantics), or document this behavior to set expectations.
🚀 Want me to fix this? Reply ex: "fix it for me".
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
apps/web/prisma/schema.prisma (1)
1023-1024: MakefolderPathnullable for consistency withfolderId.A past review identified that
folderIdandfolderPathshould be nullable for documents inPENDINGorPREVIEWstatus that haven't yet had a destination determined. WhilefolderIdwas made nullable,folderPathremains required. This inconsistency could cause issues when creating preview records before the AI determines the folder.🔎 Proposed change
// Result (after filing or prediction) folderId String? // null if AI suggests creating a new folder - folderPath String + folderPath String? fileId String?
🧹 Nitpick comments (1)
apps/web/utils/webhook/validate-webhook-account.ts (1)
110-114: Consider includinghasFilingEnabledin the return type for consistency.The
hasFilingEnabledflag is computed during validation but not included in theValidatedWebhookAccountreturn type. While callers can derive this fromemailAccount.filingEnabledandemailAccount.filingPrompt, including it would be consistent with howhasAutomationRulesis handled and avoid recomputation.🔎 Proposed change
export type ValidatedWebhookAccount = { emailAccount: NonNullable<ValidatedWebhookAccountData>; hasAutomationRules: boolean; + hasFilingEnabled: boolean; hasAiAccess: boolean; };return { success: true, data: { emailAccount, hasAutomationRules, + hasFilingEnabled, hasAiAccess: userHasAiAccess, }, };Also applies to: 197-204
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsxapps/web/app/(landing)/login/page.tsxapps/web/prisma/schema.prismaapps/web/utils/webhook/validate-webhook-account.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/(landing)/login/page.tsx
- apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/utils/webhook/validate-webhook-account.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/webhook/validate-webhook-account.tsapps/web/prisma/schema.prisma
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/webhook/validate-webhook-account.ts
**/prisma/schema.prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)
Use PostgreSQL as the database system with Prisma
Files:
apps/web/prisma/schema.prisma
🧠 Learnings (2)
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/webhook/validate-webhook-account.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/utils/webhook/validate-webhook-account.ts
⏰ 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). (3)
- GitHub Check: test
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (5)
apps/web/utils/webhook/validate-webhook-account.ts (2)
24-25: LGTM!The new filing configuration fields are correctly added to the query.
181-185: LGTM!The validation logic correctly allows webhook processing when either automation rules or filing is configured. The use of
!!filingPromptappropriately treats empty strings as disabled.apps/web/prisma/schema.prisma (3)
994-1010: LGTM!The
FilingFoldermodel is well-structured with appropriate constraints and indices.
1018-1020: Verify whether duplicate filings for the same attachment are intentional.The
DocumentFilingmodel has no unique constraint onmessageId+attachmentId, which means the same attachment could be filed multiple times. This could be intentional to support:
- Preview records (status
PREVIEW) followed by actual filing (statusFILED)- Correction workflows where the same attachment is filed to different folders
However, without constraints or application-level checks, this could lead to unintended duplicate filings.
Consider adding a composite unique constraint if duplicates are not intended, or document the design decision if they are intentional:
@@unique([emailAccountId, messageId, attachmentId, status])Or verify in the application logic that duplicate filings are properly handled.
Also applies to: 1048-1051
1180-1186: LGTM!The
DocumentFilingStatusenum has clear, well-commented values that support the filing workflow.
| filingEnabled Boolean @default(false) | ||
| filingPrompt String? |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use @db.Text for the filingPrompt field.
The filingPrompt field is intended for natural-language filing rules and instructions, which can be lengthy. Similar fields like about, writingStyle, and coldEmailPrompt use @db.Text to avoid PostgreSQL's default varchar length limits.
🔎 Proposed change
filingEnabled Boolean @default(false)
- filingPrompt String?
+ filingPrompt String? @db.Text📝 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.
| filingEnabled Boolean @default(false) | |
| filingPrompt String? | |
| filingEnabled Boolean @default(false) | |
| filingPrompt String? @db.Text |
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around lines 148-149, the filingPrompt field
should use @db.Text because it may contain long natural-language instructions;
update the filingPrompt declaration to include the Prisma native type annotation
@db.Text (matching how about, writingStyle, and coldEmailPrompt are defined) so
the column is created as TEXT in PostgreSQL and can hold arbitrary-length
content.
| accessToken String? | ||
| refreshToken String? |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use @db.Text for token fields.
OAuth access and refresh tokens can exceed PostgreSQL's default varchar length. The Account model (lines 24, 26) uses @db.Text for these fields, and DriveConnection should follow the same pattern for consistency and reliability.
🔎 Proposed change
provider String // "google" or "microsoft"
email String // can differ from emailAccount - e.g. connect work Drive to personal email
- accessToken String?
- refreshToken String?
+ accessToken String? @db.Text
+ refreshToken String? @db.Text
expiresAt DateTime?
isConnected Boolean @default(true)📝 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.
| accessToken String? | |
| refreshToken String? | |
| provider String // "google" or "microsoft" | |
| email String // can differ from emailAccount - e.g. connect work Drive to personal email | |
| accessToken String? @db.Text | |
| refreshToken String? @db.Text | |
| expiresAt DateTime? | |
| isConnected Boolean @default(true) |
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around lines 979-980, the DriveConnection
model's accessToken and refreshToken are plain String? but should use the
PostgreSQL text column type like the Account model; update both fields to
String? @db.Text to match Account and handle long OAuth tokens, then run prisma
format/generate and create a migration to apply the schema change.
| folderId String? // null if AI suggests creating a new folder | ||
| folderPath String | ||
| fileId String? | ||
| reasoning String? |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use @db.Text for the reasoning field.
The reasoning field stores AI-generated explanations for filing decisions, which can be lengthy. Use @db.Text to avoid PostgreSQL's default varchar length limits.
🔎 Proposed change
fileId String?
- reasoning String?
+ reasoning String? @db.Text
confidence Float?📝 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.
| reasoning String? | |
| fileId String? | |
| reasoning String? @db.Text | |
| confidence Float? |
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around line 1026, the reasoning String? field
should use the PostgreSQL text type to accommodate potentially long AI-generated
explanations; update the field declaration to add the @db.Text attribute (i.e.,
change it to a nullable String with @db.Text), then run prisma migrate dev (or
create a migration) and prisma generate to apply the schema change and update
the client.
Glad you like it! Hope to get it live this week! |
Without the prompt: "consent" parameter, Microsoft OAuth may not return a refresh token on re-authentication, causing token refresh failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
4 issues found across 91 files (changes from recent commits).
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name=".devcontainer/setup.sh">
<violation number="1">
P2: Suppressing stderr with `2>/dev/null` hides all error messages from prisma, making it difficult to debug setup failures. Consider removing the suppression or using a more targeted approach to handle expected non-fatal warnings.</violation>
</file>
<file name="apps/web/components/CommandK.tsx">
<violation number="1">
P2: Compose command is missing from the command palette. The refactored `actionCommands` only includes Archive when a thread is selected, but the Compose action was removed entirely. Users can still use the `C` keyboard shortcut, but the command is no longer discoverable in the palette UI.</violation>
</file>
<file name="apps/web/app/(app)/[emailAccountId]/assistant/AllRulesDisabledBanner.tsx">
<violation number="1">
P2: `rules.every()` returns `true` for empty arrays (vacuous truth). This will show the "all rules are disabled" banner even when there are no rules at all, which is misleading.</violation>
</file>
<file name=".devcontainer/docker-compose.yml">
<violation number="1">
P2: Hardcoded `SRH_TOKEN` may cause token mismatch with app's `UPSTASH_REDIS_TOKEN`. Use variable interpolation to ensure consistency.
(Based on your team's feedback about cross-service env var dependency for redis tokens.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @apps/web/utils/drive/client.ts:
- Around line 95-99: The function exchangeMicrosoftDriveCode does not validate
env.MICROSOFT_TENANT_ID before using it; update exchangeMicrosoftDriveCode to
check that env.MICROSOFT_TENANT_ID is present alongside env.MICROSOFT_CLIENT_ID
and env.MICROSOFT_CLIENT_SECRET and throw a clear Error (e.g., "Microsoft login
not enabled - missing credentials or tenant id") if any are missing so the code
that references MICROSOFT_TENANT_ID is safe.
- Around line 74-90: The getMicrosoftDriveOAuth2Url function checks
MICROSOFT_CLIENT_ID but not MICROSOFT_TENANT_ID, which can produce an
"undefined" tenant in the authorize URL; add a validation that throws a clear
Error when env.MICROSOFT_TENANT_ID is missing (similar to the existing
MICROSOFT_CLIENT_ID check) before constructing baseUrl, and reference
env.MICROSOFT_TENANT_ID in the error message so callers can see which env var is
misconfigured.
🧹 Nitpick comments (2)
apps/web/utils/webhook/process-history-item.ts (2)
206-225: Consider parallel processing for multiple attachments.The current sequential processing (using
awaitin a loop) means attachments are handled one at a time. For emails with multiple attachments, this could be slow—each attachment requires downloading, text extraction, AI analysis, and upload.Consider using
Promise.allSettled()for parallel processing, with a concurrency limiter (e.g.,p-limit) to control resource usage:🔎 Proposed parallel processing with concurrency control
+import pLimit from 'p-limit'; + +const limit = pLimit(3); // Process max 3 attachments concurrently + for (const attachment of extractableAttachments) { - await processAttachment({ + limit(() => processAttachment({ emailAccount: { ...emailAccount, filingEnabled: emailAccount.filingEnabled, filingPrompt: emailAccount.filingPrompt, email: emailAccount.email, }, message: parsedMessage, attachment, emailProvider: provider, logger, - }).catch((error) => { + })).catch((error) => { logger.error("Failed to process attachment", { filename: attachment.filename, error, }); }); }Note: This requires adding
p-limitas a dependency. Alternatively, usePromise.allSettled()without rate limiting if resource consumption isn't a concern in your serverless environment.
209-214: Remove redundant property assignments.The properties
filingEnabled,filingPrompt, and...emailAccountspread (as defined in the type at lines 29-32), making the explicit assignments redundant.🔎 Proposed fix to simplify object construction
await processAttachment({ - emailAccount: { - ...emailAccount, - filingEnabled: emailAccount.filingEnabled, - filingPrompt: emailAccount.filingPrompt, - email: emailAccount.email, - }, + emailAccount, message: parsedMessage, attachment, emailProvider: provider, logger, }).catch((error) => {
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
README.mdapps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(landing)/components/page.tsxapps/web/app/(landing)/login/page.tsxapps/web/components/ui/card.tsxapps/web/prisma/schema.prismaapps/web/utils/drive/client.tsapps/web/utils/email/google.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/utils/webhook/validate-webhook-account.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
- apps/web/utils/email/google.ts
- apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
- apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
- apps/web/utils/webhook/validate-webhook-account.ts
- apps/web/components/ui/card.tsx
🧰 Additional context used
📓 Path-based instructions (21)
*.md
📄 CodeRabbit inference engine (.cursor/rules/task-list.mdc)
*.md: Create task lists in markdown files namedTASKS.mdor with a descriptive feature-specific name (e.g.,ASSISTANT_CHAT.md) in the project root to track project progress
Structure task list markdown files with sections: Feature Name Implementation (title), description, Completed Tasks, In Progress Tasks, Future Tasks, Implementation Plan, and Relevant Files subsections
Update task list markdown files by marking tasks as completed with[x], adding new identified tasks, and moving tasks between Completed/In Progress/Future sections as appropriate
Keep the 'Relevant Files' section in task list markdown files updated with file paths that have been created or modified, brief descriptions of each file's purpose, and status indicators (e.g., ✅) for completed components
Files:
README.md
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
README.mdapps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/prisma/schema.prismaapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
Don't export imported variables
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions
Don't use TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include atitleelement for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
AssigntabIndexto non-interactive HTML elements witharia-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/drive/client.tsapps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.ts
**/prisma/schema.prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)
Use PostgreSQL as the database system with Prisma
Files:
apps/web/prisma/schema.prisma
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
apps/web/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor error handling in forms
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
**/{pages,routes,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)
Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Files:
apps/web/app/(landing)/components/page.tsx
🧠 Learnings (42)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to apps/web/env.ts : Add client-side environment variables to `apps/web/env.ts` under the `experimental__runtimeEnv` object to enable runtime access
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details
Applied to files:
apps/web/utils/drive/client.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/utils/outlook/message.tsapps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/utils/outlook/message.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/outlook/message.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error
Applied to files:
apps/web/utils/outlook/message.ts
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:42:16.602Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:16.602Z
Learning: The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : System prompts must define the LLM's role and task specifications
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure ARIA properties are valid for the element's supported roles
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use semantic elements instead of role attributes in JSX
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use valid, non-abstract ARIA roles for elements with ARIA roles
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't assign non-interactive ARIA roles to interactive HTML elements
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Give all elements requiring alt text meaningful information for screen readers
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't add ARIA roles, states, and properties to elements that don't support them
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure label elements have text content and are associated with an input
Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(landing)/login/page.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.
Applied to files:
apps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.tsx : For text areas, use the `Input` component with `type='text'`, `autosizeTextarea` prop set to true, and `registerProps` for form integration
Applied to files:
apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use Shadcn UI and Tailwind for components and styling
Applied to files:
apps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory
Applied to files:
apps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use Shadcn UI and Tailwind for components and styling
Applied to files:
apps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Follow consistent naming conventions using PascalCase for components
Applied to files:
apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/**/*.tsx : Use PascalCase for component file names (e.g., `components/Button.tsx`)
Applied to files:
apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(landing)/login/page.tsxapps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications
Applied to files:
apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use shadcn/ui components when available
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props
Applied to files:
apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states
Applied to files:
apps/web/app/(landing)/components/page.tsx
🧬 Code graph analysis (3)
apps/web/utils/drive/client.ts (1)
apps/web/utils/drive/scopes.ts (2)
GOOGLE_DRIVE_SCOPES(18-24)MICROSOFT_DRIVE_SCOPES(4-13)
apps/web/utils/webhook/process-history-item.ts (4)
apps/web/utils/filebot/is-filebot-email.ts (1)
isFilebotEmail(13-38)apps/web/utils/drive/handle-filing-reply.ts (1)
processFilingReply(32-173)apps/web/utils/premium/index.ts (1)
hasAiAccess(99-115)apps/web/utils/drive/filing-engine.ts (1)
processAttachment(65-292)
apps/web/app/(landing)/login/page.tsx (1)
apps/web/components/Typography.tsx (1)
MutedText(142-142)
⏰ 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). (3)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: test
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (23)
README.md (3)
46-47: Feature list additions are well-formatted and consistent.Both new features are properly documented with status indicators (Beta, Early Access) that signal their maturity level to users.
173-173: Google Drive OAuth documentation is complete and consistent.The callback URI, scope, and API enablement are all properly documented and follow the existing pattern for optional integrations (similar to calendar integration).
Also applies to: 192-192, 206-206
253-253: Microsoft OAuth documentation for OneDrive is properly configured.The callback URI and Files.ReadWrite permission are correctly documented and follow the established pattern for optional integrations.
Also applies to: 283-283
apps/web/app/(landing)/login/page.tsx (1)
12-12: LGTM! Clean refactoring to centralize muted text styling.The replacement of
<p>elements with theMutedTextcomponent improves consistency across the codebase by centralizing the styling for muted text. The spacing and alignment classes are preserved correctly.Also applies to: 51-67, 69-79
apps/web/app/(landing)/components/page.tsx (4)
4-12: LGTM! Properly expanded imports for component demonstrations.The expanded Card component imports and addition of
PageSubHeadingandMutedTextto the Typography imports are appropriate for the new demonstration examples being added to this storybook page.Also applies to: 16-16, 24-24
77-77: LGTM! Typography examples added correctly.The addition of
PageSubHeadingandMutedTextexamples in the Typography section properly demonstrates these components alongside the existing typography elements.Also applies to: 81-81
88-129: LGTM! Excellent demonstration of Card component size variants.The side-by-side grid showing the default and small (
size="sm") card variants effectively demonstrates the Card component's size prop feature. The examples properly showcase:
- Different card sizes with appropriate padding
- Complete card structure (Header, Content, Footer)
- Responsive grid layout with
md:grid-cols-2- Consistent button sizing matching card size
288-290: LGTM! Consistent application of MutedText component.The replacement of plain text elements with
MutedTextthroughout the demo sections improves consistency and aligns with the broader refactoring effort to centralize muted text styling across the codebase.Also applies to: 297-297, 304-304, 446-448, 638-638, 644-644, 651-651, 672-672, 751-751, 763-763, 775-775, 789-791, 806-808
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (1)
3-3: LGTM! Clean refactoring for visual consistency.The introduction of
MutedTextfor the descriptive text improves visual hierarchy and aligns with the PR's broader UI standardization effort. The change is straightforward and maintains the same content while applying consistent styling.Also applies to: 10-13
apps/web/utils/drive/client.ts (4)
12-18: LGTM!The OAuth2 client creation using the official
@googleapis/drivelibrary is well-structured and properly utilizes environment variables for configuration.
23-31: LGTM!Good use of
access_type: "offline"andprompt: "consent"to ensure refresh tokens are returned consistently.
36-65: LGTM!Solid implementation with proper token validation and secure ID token verification to extract the user's email.
118-127: LGTM!Good error handling pattern—checking
response.okbefore parsing the success response, with a safe fallback for non-JSON error bodies.apps/web/utils/webhook/process-history-item.ts (3)
1-1: LGTM! Imports are appropriate for the filing feature.The imports correctly bring in Next.js 15's
afterfor background processing and the necessary filing utilities.Also applies to: 7-12
29-32: LGTM! Type extension is correct.The extension properly adds the filing-related fields needed by the new functionality.
114-128: LGTM! Filebot detection and early return are correctly implemented.The code properly delegates to
isFilebotEmail, which handles multiple recipients in the To field internally (splits by comma and checks each). The early return pattern keeps the code clean.apps/web/utils/outlook/message.ts (4)
12-12: LGTM: hasAttachments field addition is correct.The
hasAttachmentsfield is a standard Microsoft Graph message property and is properly added to the select fields.
567-583: LGTM: Attachment processing logic is robust.The filter on line 570 correctly ensures only file attachments with valid IDs are processed. The type guard properly narrows the type, making line 576 (
attachmentId: att.id) type-safe. Default values for missing fields are appropriate.
619-625: LGTM: Internal type definition follows guidelines.The
OutlookAttachmenttype correctly models the Microsoft Graph attachment structure and is appropriately kept internal (not exported), following the coding guideline to avoid exporting types used only within the same file.
439-439: Fetch folder IDs to properly map message labels.Passing empty
{}forfolderIdswill cause messages to lack proper label information (INBOX, SENT, ARCHIVE, etc.). All other query functions in this file callgetFolderIds(client, logger)and pass the result toconvertMessages.Based on past review comments, this issue has been flagged multiple times and should be addressed.
Also applies to: 453-453
Likely an incorrect or invalid review comment.
apps/web/prisma/schema.prisma (3)
994-1010: Well-designed FilingFolder model.The model structure is solid:
- Appropriate unique constraint on
[emailAccountId, folderId]prevents duplicate folder tracking- Proper indexes on foreign keys for query performance
- Correct cascade delete behavior to maintain referential integrity
1180-1186: Well-documented enum with clear state definitions.The
DocumentFilingStatusenum clearly defines all filing lifecycle states with helpful inline comments. The states logically cover preview, user decision points, success, and error cases.
1012-1052: DocumentFiling schema is properly designed; folderPath nullability concern is unfounded.The
folderPathfield correctly remains non-nullable. Analysis of the filing engine reveals thatfolderPathis always populated before creating a DocumentFiling record, even for PENDING status documents:
- When using existing folders:
folderPathis set tofolder.path || folder.name- When creating new folders:
folderPathis set toanalysis.folderPath || "Inbox Zero Filed"The
|| "Inbox Zero Filed"fallback ensures a default path is always available, even when the AI hasn't explicitly determined a destination. PENDING status documents therefore always have a determined folder path ready for user confirmation.The
folderIdnullability improvement is appropriate for cases where new folders are being created.
| export function getMicrosoftDriveOAuth2Url(state: string): string { | ||
| if (!env.MICROSOFT_CLIENT_ID) { | ||
| throw new Error("Microsoft login not enabled - missing client ID"); | ||
| } | ||
|
|
||
| const baseUrl = `https://login.microsoftonline.com/${env.MICROSOFT_TENANT_ID}/oauth2/v2.0/authorize`; | ||
| const params = new URLSearchParams({ | ||
| client_id: env.MICROSOFT_CLIENT_ID, | ||
| response_type: "code", | ||
| redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/drive/callback`, | ||
| scope: MICROSOFT_DRIVE_SCOPES.join(" "), | ||
| prompt: "consent", // Ensures refresh token is returned on re-auth | ||
| state, | ||
| }); | ||
|
|
||
| return `${baseUrl}?${params.toString()}`; | ||
| } |
There was a problem hiding this comment.
Add validation for MICROSOFT_TENANT_ID.
The function validates MICROSOFT_CLIENT_ID but uses MICROSOFT_TENANT_ID in the URL without validation. If undefined, the URL would contain the literal string "undefined", causing confusing OAuth failures.
🔎 Proposed fix
export function getMicrosoftDriveOAuth2Url(state: string): string {
- if (!env.MICROSOFT_CLIENT_ID) {
- throw new Error("Microsoft login not enabled - missing client ID");
+ if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_TENANT_ID) {
+ throw new Error("Microsoft login not enabled - missing client ID or tenant ID");
}🤖 Prompt for AI Agents
In @apps/web/utils/drive/client.ts around lines 74 - 90, The
getMicrosoftDriveOAuth2Url function checks MICROSOFT_CLIENT_ID but not
MICROSOFT_TENANT_ID, which can produce an "undefined" tenant in the authorize
URL; add a validation that throws a clear Error when env.MICROSOFT_TENANT_ID is
missing (similar to the existing MICROSOFT_CLIENT_ID check) before constructing
baseUrl, and reference env.MICROSOFT_TENANT_ID in the error message so callers
can see which env var is misconfigured.
| export async function exchangeMicrosoftDriveCode(code: string) { | ||
| if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET) { | ||
| throw new Error("Microsoft login not enabled - missing credentials"); | ||
| } | ||
|
|
There was a problem hiding this comment.
Add validation for MICROSOFT_TENANT_ID.
Same issue as the URL generation function—MICROSOFT_TENANT_ID is used in line 101 but not validated alongside the other credentials.
🔎 Proposed fix
export async function exchangeMicrosoftDriveCode(code: string) {
- if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET) {
- throw new Error("Microsoft login not enabled - missing credentials");
+ if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET || !env.MICROSOFT_TENANT_ID) {
+ throw new Error("Microsoft login not enabled - missing credentials or tenant ID");
}🤖 Prompt for AI Agents
In @apps/web/utils/drive/client.ts around lines 95 - 99, The function
exchangeMicrosoftDriveCode does not validate env.MICROSOFT_TENANT_ID before
using it; update exchangeMicrosoftDriveCode to check that
env.MICROSOFT_TENANT_ID is present alongside env.MICROSOFT_CLIENT_ID and
env.MICROSOFT_CLIENT_SECRET and throw a clear Error (e.g., "Microsoft login not
enabled - missing credentials or tenant id") if any are missing so the code that
references MICROSOFT_TENANT_ID is safe.
User description
Summary by CodeRabbit
New Features
Improvements
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.
Note
Smart Filing (Drive/OneDrive) feature
/driveexperience: onboarding, setup (folder selection + natural-language rules), preview with per-file feedback/move, preferences, and recent activityfilingEnabled/filingPromptDriveConnection,FilingFolder,DocumentFiling(with preview/feedback fields), and newEmailAccount.filingEnabled/filingPromptcolumnsMutedText; SideNav entry and feature flag/envNEXT_PUBLIC_SMART_FILING_ENABLED@googleapis/drive,mammoth,unpdf; register@kibo-uiWritten by Cursor Bugbot for commit c955629. Configure here.
Generated description
Below is a concise technical summary of the changes proposed in this PR:
graph LR GOOGLEAPIS_DRIVE_CLIENT_("GOOGLEAPIS_DRIVE_CLIENT"):::added GOOGLE_DRIVE_API_("GOOGLE_DRIVE_API"):::added MICROSOFT_GRAPH_CLIENT_("MICROSOFT_GRAPH_CLIENT"):::added MICROSOFT_GRAPH_API_("MICROSOFT_GRAPH_API"):::modified GOOGLE_OAUTH_("GOOGLE_OAUTH"):::added PRISMA_DB_("PRISMA_DB"):::modified MICROSOFT_OAUTH_("MICROSOFT_OAUTH"):::added REDIS_("REDIS"):::modified GOOGLEAPIS_DRIVE_CLIENT_ -- "Sends attachment upload and folder-listing requests for previews." --> GOOGLE_DRIVE_API_ GOOGLE_DRIVE_API_ -- "Returns folder listings and upload confirmations to client." --> GOOGLEAPIS_DRIVE_CLIENT_ MICROSOFT_GRAPH_CLIENT_ -- "Requests OneDrive folder listings and file uploads for auto-filing." --> MICROSOFT_GRAPH_API_ MICROSOFT_GRAPH_API_ -- "Provides folder/file metadata and upload/move responses." --> MICROSOFT_GRAPH_CLIENT_ GOOGLE_OAUTH_ -- "Stores Google access/refresh tokens in Prisma driveConnection." --> PRISMA_DB_ MICROSOFT_OAUTH_ -- "Persists Microsoft OAuth tokens to Prisma driveConnection records." --> PRISMA_DB_ GOOGLE_OAUTH_ -- "Caches OAuth code results and acquires locks to deduplicate." --> REDIS_ MICROSOFT_OAUTH_ -- "Uses Redis lock/cache to prevent duplicate OAuth processing." --> REDIS_ classDef added stroke:#15AA7A classDef removed stroke:#CD5270 classDef modified stroke:#EDAC4C linkStyle default stroke:#CBD5E1,font-size:13pxImplements a new AI-powered Smart Filing feature, enabling automatic organization of email attachments into Google Drive and OneDrive. Integrates OAuth flows for secure drive connections and provides user interfaces for setup, folder selection, filing preview, and activity tracking.
MutedTextcomponent for consistent styling of muted text and introduces asizeprop forCardcomponents to allow for more compact layouts.Modified files (31)
Latest Contributors(2)
captureExceptionutility for error reporting and removes direct logger calls for error initiation, improving consistency in error handling.Modified files (2)
Latest Contributors(2)
StatsCard.tsxcomponent, indicating a consolidation or removal of a specific statistics display component.Modified files (1)
Latest Contributors(1)
DriveConnection,FilingFolder, andDocumentFiling.Modified files (89)
Checks (1)
Latest Contributors(2)