Conversation
|
@edulelis is attempting to deploy a commit to the Inbox Zero Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. WalkthroughAdds an account-scoped "Report" debug button and page; implements a server action to fetch Gmail data and run multiple AI analyses (summarization, persona, behavior, response patterns, label optimization, recommendations); migrates several LLM calls to createGenerateObject/generateObject with trace logging and minor utility/comment tweaks. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant DebugPage as Debug Report Page (Client)
participant Action as generateReportAction (Server)
participant Report as getEmailReportData
participant Gmail as Gmail API
participant AI as LLM
User->>DebugPage: Click "Generate Report"
DebugPage->>Action: invoke with { emailAccountId }
Action->>Report: getEmailReportData(id)
Report->>Gmail: Fetch emails, labels, signature, templates (with retries/backoff)
Gmail-->>Report: Data sets
Report->>AI: aiSummarizeEmails (batched)
AI-->>Report: Email summaries
Report->>AI: Run analyses (executive summary, persona, behavior, responses, labels, recommendations)
AI-->>Report: Structured analyses
Report-->>Action: Consolidated report object
Action-->>DebugPage: Return report
DebugPage-->>User: Render report UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: .coderabbit.yaml 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (9)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (3)
apps/web/app/(reports)/layout.tsx (1)
56-66: Consistent navigation for sign upThe sign up button currently redirects to
/logininstead of a dedicated sign up page.Consider using a dedicated sign up route for clarity:
<Button asChild size="sm"> - <Link href="/login" className="flex items-center gap-1"> + <Link href="/signup" className="flex items-center gap-1"> <UserPlus className="h-3 w-3" /> Sign Up </Link> </Button>apps/web/app/(reports)/email-analysis/page.tsx (2)
146-146: Use absolute URL for API endpointThe API endpoint should use an absolute path to ensure it works correctly in all environments.
- const response = await fetch("/api/email-analysis", { + const response = await fetch("/reports/api/email-analysis", {
207-212: Consider using React Hook Form for better form handlingAccording to coding guidelines, forms should use React Hook Form with Zod validation.
import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; const FormSchema = z.object({ email: z.string().email("Please enter a valid email address"), }); const form = useForm({ resolver: zodResolver(FormSchema), defaultValues: { email: "" }, }); // Use form.handleSubmit(handleAnalyze) and form.register("email")
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/web/app/(reports)/api/email-analysis/route.ts(1 hunks)apps/web/app/(reports)/api/email-summaries/route.ts(1 hunks)apps/web/app/(reports)/api/gmail-labels/route.ts(1 hunks)apps/web/app/(reports)/email-analysis/page.tsx(1 hunks)apps/web/app/(reports)/layout.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/api/email-analysis/route.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
**/api/**/route.ts
📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/api/email-analysis/route.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(reports)/api/gmail-labels/route.tsapps/web/app/(reports)/layout.tsxapps/web/app/(reports)/api/email-summaries/route.tsapps/web/app/(reports)/email-analysis/page.tsxapps/web/app/(reports)/api/email-analysis/route.ts
apps/web/**/*.tsx
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(reports)/layout.tsxapps/web/app/(reports)/email-analysis/page.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(reports)/layout.tsxapps/web/app/(reports)/email-analysis/page.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(reports)/layout.tsxapps/web/app/(reports)/email-analysis/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(reports)/layout.tsxapps/web/app/(reports)/email-analysis/page.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on 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 assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
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 a title element 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.
Assign tabIndex to non-interactive HTML elements with aria-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 a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(reports)/layout.tsxapps/web/app/(reports)/email-analysis/page.tsx🧠 Learnings (6)
📓 Common learnings
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/utilities.mdc:0-0 Timestamp: 2025-07-19T17:50:28.270Z Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Organize files according to the prescribed structure: API routes in `app/api/`, validation schemas in `utils/actions/*.validation.ts`, server actions in `utils/actions/`, SWR hooks in `hooks/`, and form components in `components/`.Learnt from: elie222 PR: elie222/inbox-zero#537 File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34 Timestamp: 2025-07-08T13:14:07.449Z Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.apps/web/app/(reports)/api/gmail-labels/route.ts (16)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler withwithAuthorwithEmailAccountfor consistent error handling and authentication in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, aswithAuthandwithEmailAccounthandle error catching.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses usingNextResponse.json()in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: Theutilsfolder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actionsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.jsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use theswrpackageLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use theswrpackageLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routesapps/web/app/(reports)/layout.tsx (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either inpage.tsxor in theapps/web/app/(app)/PAGE_NAMEfolderLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folderLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async dataLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loadingLearnt from: elie222
PR: #485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use theuseSearchParamshook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at:apps/web/app/(app)/PAGE_NAME/page.tsxLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : UseLoadingContentcomponent to handle loading and error states consistentlyLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use Shadcn UI and Tailwind for components and stylingLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Ensure responsive design with mobile-first approachLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.apps/web/app/(reports)/api/email-summaries/route.ts (12)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses usingNextResponse.json()in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Do NOT use POST API routes for mutations - use server actions insteadLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler withwithAuthorwithEmailAccountfor consistent error handling and authentication in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: Theutilsfolder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actionsapps/web/app/(reports)/email-analysis/page.tsx (18)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at:apps/web/app/(app)/PAGE_NAME/page.tsxLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either inpage.tsxor in theapps/web/app/(app)/PAGE_NAMEfolderLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loadingLearnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsxLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folderLearnt from: edulelis
PR: #576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses[key: string]: DigestItem[] | undefined | string | Date | undefinedinstead of intersection types like& Record<string, DigestItem[] | undefined>due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/components/**/*.tsx : Use PascalCase for components (e.g.components/Button.tsx)Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async dataLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : UseLoadingContentcomponent to handle loading and error states consistentlyLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use theLoadingContentcomponent to handle loading and error states consistently in data-fetching components.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use theLoadingContentcomponent to handle loading statesLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Implement responsive design with Tailwind CSS using a mobile-first approachLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components so you can load data into them directlyLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use Shadcn UI and Tailwind for components and stylingLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooksapps/web/app/(reports)/api/email-analysis/route.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: Theutilsfolder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler withwithAuthorwithEmailAccountfor consistent error handling and authentication in GET API routes.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (4)
apps/web/app/(reports)/api/gmail-labels/route.ts (1)
1-94: Export response type for type-safe client usageAccording to the coding guidelines, GET API routes should export response types for type-safe client usage.
Add a response type export at the top of the file:
export type GmailLabelsResponse = { labels: Array<{ id: string; name: string; messagesTotal: number; messagesUnread: number; color: string | null; type: string; }>; totalLabels: number; };⛔ Skipped due to learnings
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.Learnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routesLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/get-api-route.mdc:0-0 Timestamp: 2025-07-18T15:05:26.713Z Learning: Applies to app/api/**/route.ts : Infer and export the response type for GET API routes using `Awaited<ReturnType<typeof getData>>` as shown in the example.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{ts,tsx} : Use export type for types.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/get-api-route.mdc:0-0 Timestamp: 2025-07-18T15:05:26.713Z Learning: Applies to app/api/**/route.ts : Return responses using `NextResponse.json()` in GET API routes.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/gmail-api.mdc:0-0 Timestamp: 2025-07-18T15:05:34.899Z Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/security.mdc:0-0 Timestamp: 2025-07-18T17:27:46.389Z Learning: Applies to **/api/**/route.ts : API routes MUST use a consistent error response format, returning JSON with an error message and status code.Learnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/**/types/**/*.ts : Centralize types in dedicated type files when sharedLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-07-18T15:06:40.272Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-07-18T15:06:40.272Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/index.mdc:0-0 Timestamp: 2025-07-18T15:05:56.644Z Learning: Guidelines for implementing Next.js server actionsLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/utilities.mdc:0-0 Timestamp: 2025-07-19T17:50:28.270Z Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/index.mdc:0-0 Timestamp: 2025-07-18T15:05:56.644Z Learning: Guidelines for implementing GET API routes in Next.jsLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/data-fetching.mdc:0-0 Timestamp: 2025-07-18T15:04:44.818Z Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the `swr` packageLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ui-components.mdc:0-0 Timestamp: 2025-07-19T17:50:22.078Z Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use the `swr` packageapps/web/app/(reports)/layout.tsx (1)
1-131: Well-structured layout with proper authenticationThe layout correctly implements authentication and admin authorization checks, following security best practices. The UI is clean and responsive.
apps/web/app/(reports)/email-analysis/page.tsx (2)
1-627: Well-structured UI with comprehensive type safetyThe component has excellent TypeScript interfaces and a clean, responsive UI layout. The report display is well-organized with clear sections and visual hierarchy.
141-166: Use SWR for API calls and LoadingContent for statesAccording to the coding guidelines, client components should use SWR for API GET requests and LoadingContent for handling loading/error states.
Refactor to use SWR and proper loading states:
import useSWR from "swr"; import { LoadingContent } from "@/components/LoadingContent"; import { toastError, toastSuccess } from "@/components/Toast"; // At the top of the component const { data, error, isLoading, mutate } = useSWR( email ? `/api/email-analysis?email=${encodeURIComponent(email)}` : null, fetcher, { revalidateOnFocus: false } ); const handleAnalyze = async () => { if (!email.trim()) return; try { await mutate(); toastSuccess({ description: "Report generated successfully" }); } catch (error) { toastError({ description: "Failed to generate report" }); } }; // In the render return ( <LoadingContent loading={isLoading} error={error}> {/* existing content */} </LoadingContent> );⛔ Skipped due to learnings
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/hooks/**/*.ts : Use SWR for client-side data fetching and caching.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/components/**/*.tsx : Use the `LoadingContent` component to handle loading and error states consistently in data-fetching components.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ui-components.mdc:0-0 Timestamp: 2025-07-19T17:50:22.078Z Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use the `swr` packageLearnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/hooks/**/*.ts : Use SWR for efficient data fetching and cachingLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/data-fetching.mdc:0-0 Timestamp: 2025-07-18T15:04:44.818Z Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the `swr` packageLearnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/**/*.tsx : Use `LoadingContent` component to handle loading and error states consistentlyLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/hooks.mdc:0-0 Timestamp: 2025-07-18T15:05:41.705Z Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : For fetching data from API endpoints in custom hooks, prefer using `useSWR`.Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/page-structure.mdc:0-0 Timestamp: 2025-07-18T15:07:00.269Z Learning: Applies to apps/web/app/(app)/*/**/**/*.tsx : If we're in a deeply nested component we will use swr to fetch via APILearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/security-audit.mdc:0-0 Timestamp: 2025-07-20T09:00:41.968Z Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.Learnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async dataLearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/index.mdc:0-0 Timestamp: 2025-07-18T15:05:56.644Z Learning: Fetching data from the API using SWR
| async function fetchEmailsByQuery( | ||
| gmail: any, | ||
| query: string, | ||
| count: number, | ||
| ): Promise<any[]> { | ||
| const emails: any[] = []; | ||
| let nextPageToken: string | undefined; | ||
| let retryCount = 0; | ||
| const maxRetries = 3; | ||
|
|
||
| while (emails.length < count && retryCount < maxRetries) { | ||
| try { | ||
| const response = await getMessages(gmail, { | ||
| query: query || undefined, | ||
| maxResults: Math.min(100, count - emails.length), | ||
| pageToken: nextPageToken, | ||
| }); | ||
|
|
||
| if (!response.messages || response.messages.length === 0) { | ||
| break; | ||
| } | ||
|
|
||
| // Get full message details for each email with retry logic | ||
| const messagePromises = response.messages.map(async (message: any) => { | ||
| if (!message.id) return null; | ||
|
|
||
| for (let i = 0; i < 3; i++) { | ||
| try { | ||
| const messageWithPayload = await getMessage( | ||
| message.id, | ||
| gmail, | ||
| "full", | ||
| ); | ||
| return parseMessage(messageWithPayload); | ||
| } catch (error) { | ||
| if (i === 2) { | ||
| console.warn( | ||
| `Failed to fetch message ${message.id} after 3 attempts:`, | ||
| error, | ||
| ); | ||
| return null; | ||
| } | ||
| // Wait before retry | ||
| await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1))); | ||
| } | ||
| } | ||
| return null; | ||
| }); | ||
|
|
||
| const messages = await Promise.all(messagePromises); | ||
| const validMessages = messages.filter((msg) => msg !== null); | ||
|
|
||
| emails.push(...validMessages); | ||
|
|
||
| nextPageToken = response.nextPageToken || undefined; | ||
| if (!nextPageToken) { | ||
| break; | ||
| } | ||
|
|
||
| retryCount = 0; // Reset retry count on successful request | ||
| } catch (error) { | ||
| retryCount++; | ||
| console.warn( | ||
| `Gmail API error (attempt ${retryCount}/${maxRetries}):`, | ||
| error, | ||
| ); | ||
|
|
||
| if (retryCount >= maxRetries) { | ||
| console.error( | ||
| `Failed to fetch emails after ${maxRetries} attempts:`, | ||
| error, | ||
| ); | ||
| break; | ||
| } | ||
|
|
||
| // Wait before retry with exponential backoff | ||
| await new Promise((resolve) => setTimeout(resolve, 2000 * retryCount)); | ||
| } | ||
| } | ||
|
|
||
| return emails; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Code duplication: fetchEmailsByQuery function
This function is duplicated from the email-summaries route. Consider extracting it to a shared utility.
Create a shared utility file:
// apps/web/utils/gmail/fetch-emails.ts
export async function fetchEmailsByQuery(
gmail: any,
query: string,
count: number,
): Promise<any[]> {
// ... existing implementation
}Then import and use it in both files:
-async function fetchEmailsByQuery(
- gmail: any,
- query: string,
- count: number,
-): Promise<any[]> {
- // ... implementation
-}
+import { fetchEmailsByQuery } from "@/utils/gmail/fetch-emails";🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts lines 558 to 639, the
fetchEmailsByQuery function is duplicated from another route. To fix this,
extract the entire fetchEmailsByQuery function into a new shared utility file,
for example, apps/web/utils/gmail/fetch-emails.ts, exporting it as a named
function. Then, remove the duplicate function from this file and import the
shared function where needed to avoid code duplication and improve
maintainability.
| async function fetchGmailLabels(gmail: any): Promise<any[]> { | ||
| try { | ||
| const response = await gmail.users.labels.list({ userId: "me" }); | ||
|
|
||
| // Filter out system labels, keep only user-created labels | ||
| const userLabels = | ||
| response.data.labels?.filter( | ||
| (label: any) => | ||
| label.type === "user" && | ||
| !label.name.startsWith("CATEGORY_") && | ||
| !label.name.startsWith("CHAT"), | ||
| ) || []; | ||
|
|
||
| // Get detailed info for each label to get message counts | ||
| const labelsWithCounts = await Promise.all( | ||
| userLabels.map(async (label: any) => { | ||
| try { | ||
| const labelDetail = await gmail.users.labels.get({ | ||
| userId: "me", | ||
| id: label.id, | ||
| }); | ||
| return { | ||
| ...label, | ||
| messagesTotal: labelDetail.data.messagesTotal || 0, | ||
| messagesUnread: labelDetail.data.messagesUnread || 0, | ||
| threadsTotal: labelDetail.data.threadsTotal || 0, | ||
| threadsUnread: labelDetail.data.threadsUnread || 0, | ||
| }; | ||
| } catch (error) { | ||
| console.warn(`Failed to get details for label ${label.name}:`, error); | ||
| return { | ||
| ...label, | ||
| messagesTotal: 0, | ||
| messagesUnread: 0, | ||
| threadsTotal: 0, | ||
| threadsUnread: 0, | ||
| }; | ||
| } | ||
| }), | ||
| ); | ||
|
|
||
| return labelsWithCounts; | ||
| } catch (error) { | ||
| console.warn("Failed to fetch Gmail labels:", error); | ||
| return []; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Code duplication: fetchGmailLabels function
This functionality is already implemented in the gmail-labels route and duplicated here.
Extract to a shared utility and reuse:
// apps/web/utils/gmail/labels.ts
export async function fetchGmailLabels(gmail: any): Promise<GmailLabel[]> {
// ... existing implementation
}🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts lines 644 to 690, the
fetchGmailLabels function duplicates existing functionality found in the
gmail-labels route. To fix this, extract the fetchGmailLabels function into a
shared utility file such as apps/web/utils/gmail/labels.ts, then import and
reuse this utility function in both places instead of duplicating the code. This
will centralize the logic and reduce code duplication.
| export async function POST(request: NextRequest) { | ||
| const startTime = Date.now(); | ||
| const requestId = Math.random().toString(36).substring(7); | ||
|
|
||
| console.log( | ||
| `[${requestId}] Starting comprehensive analysis for user: ${request.url}`, | ||
| ); | ||
|
|
||
| try { | ||
| const body = await request.json(); | ||
| const { userEmail } = body; | ||
|
|
||
| if (!userEmail) { | ||
| console.log(`[${requestId}] Error: No userEmail provided`); | ||
| return NextResponse.json( | ||
| { error: "Email address is required" }, | ||
| { status: 400 }, | ||
| ); | ||
| } | ||
|
|
||
| console.log(`[${requestId}] Processing analysis for: ${userEmail}`); | ||
|
|
||
| // Fetch user's email account | ||
| const emailAccount = await prisma.emailAccount.findFirst({ | ||
| where: { user: { email: userEmail } }, | ||
| include: { account: true }, | ||
| }); | ||
|
|
||
| if (!emailAccount) { | ||
| console.log( | ||
| `[${requestId}] Error: Email account not found for ${userEmail}`, | ||
| ); | ||
| return NextResponse.json( | ||
| { error: "Email account not found" }, | ||
| { status: 404 }, | ||
| ); | ||
| } | ||
|
|
||
| console.log(`[${requestId}] Found email account: ${emailAccount.email}`); | ||
|
|
||
| // Get Gmail client | ||
| console.log(`[${requestId}] Initializing Gmail client...`); | ||
| const gmail = await getGmailClientWithRefresh({ | ||
| accessToken: emailAccount.account?.access_token ?? "", | ||
| refreshToken: emailAccount.account?.refresh_token ?? "", | ||
| expiresAt: emailAccount.account?.expires_at, | ||
| emailAccountId: emailAccount.id, | ||
| }); | ||
| console.log(`[${requestId}] Gmail client initialized successfully`); | ||
|
|
||
| // Fetch raw emails to get date information | ||
| console.log(`[${requestId}] Fetching raw emails for date analysis...`); | ||
| const [receivedEmails, sentEmails] = await Promise.all([ | ||
| fetchEmailsByQuery(gmail, "", 200), | ||
| fetchEmailsByQuery(gmail, "from:me", 50), | ||
| ]); | ||
| console.log( | ||
| `[${requestId}] Fetched ${receivedEmails.length} received emails, ${sentEmails.length} sent emails`, | ||
| ); | ||
|
|
||
| // Get date range from actual emails | ||
| const allEmails = [...receivedEmails, ...sentEmails]; | ||
| const emailDates = allEmails | ||
| .map((email) => | ||
| email.headers?.date ? new Date(email.headers.date) : null, | ||
| ) | ||
| .filter((date) => date !== null) | ||
| .sort((a, b) => a!.getTime() - b!.getTime()); | ||
|
|
||
| const oldestDate = | ||
| emailDates.length > 0 | ||
| ? emailDates[0] | ||
| : new Date(Date.now() - 90 * 24 * 60 * 60 * 1000); | ||
| const newestDate = | ||
| emailDates.length > 0 ? emailDates[emailDates.length - 1] : new Date(); | ||
| const totalDays = Math.ceil( | ||
| (newestDate.getTime() - oldestDate.getTime()) / (24 * 60 * 60 * 1000), | ||
| ); | ||
|
|
||
| // Fetch email summaries from the email-summaries endpoint | ||
| console.log(`[${requestId}] Fetching email summaries...`); | ||
| const emailSummariesUrl = `${request.url.replace("/comprehensive-analysis", "/email-summaries")}`; | ||
|
|
||
| // Create AbortController for timeout handling | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 minute timeout | ||
|
|
||
| let receivedResponse: Response, sentResponse: Response; | ||
| try { | ||
| [receivedResponse, sentResponse] = await Promise.all([ | ||
| fetch(emailSummariesUrl, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ userEmail, count: 200 }), | ||
| signal: controller.signal, | ||
| }), | ||
| fetch(emailSummariesUrl, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ userEmail, query: "from:me", count: 50 }), | ||
| signal: controller.signal, | ||
| }), | ||
| ]); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| // Check for HTTP errors | ||
| if (!receivedResponse.ok) { | ||
| const errorText = await receivedResponse.text(); | ||
| console.error(`[${requestId}] Received emails API error:`, { | ||
| status: receivedResponse.status, | ||
| statusText: receivedResponse.statusText, | ||
| body: errorText.substring(0, 500), | ||
| }); | ||
| throw new Error( | ||
| `Failed to fetch received emails: ${receivedResponse.status} ${receivedResponse.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| if (!sentResponse.ok) { | ||
| const errorText = await sentResponse.text(); | ||
| console.error(`[${requestId}] Sent emails API error:`, { | ||
| status: sentResponse.status, | ||
| statusText: sentResponse.statusText, | ||
| body: errorText.substring(0, 500), | ||
| }); | ||
| throw new Error( | ||
| `Failed to fetch sent emails: ${sentResponse.status} ${sentResponse.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| const receivedData = await receivedResponse.json(); | ||
| const sentData = await sentResponse.json(); | ||
|
|
||
| console.log(`[${requestId}] Email summaries fetched successfully:`, { | ||
| receivedCount: receivedData.summaries?.length || 0, | ||
| sentCount: sentData.summaries?.length || 0, | ||
| }); | ||
|
|
||
| // Fetch additional Gmail data | ||
| console.log(`[${requestId}] Fetching additional Gmail data...`); | ||
| const [gmailLabels, gmailSignature, gmailTemplates] = await Promise.all([ | ||
| fetchGmailLabels(gmail), | ||
| fetchGmailSignature(gmail), | ||
| fetchGmailTemplates(gmail), | ||
| ]); | ||
| console.log(`[${requestId}] Gmail data fetched:`, { | ||
| labelsCount: gmailLabels.length, | ||
| hasSignature: !!gmailSignature, | ||
| templatesCount: gmailTemplates.length, | ||
| }); | ||
|
|
||
| // Run all analysis functions | ||
| console.log(`[${requestId}] Starting AI analysis functions...`); | ||
| const [ | ||
| executiveSummary, | ||
| userPersona, | ||
| emailBehavior, | ||
| responsePatterns, | ||
| labelAnalysis, | ||
| ] = await Promise.all([ | ||
| generateExecutiveSummary( | ||
| receivedData.summaries, | ||
| sentData.summaries, | ||
| gmailLabels, | ||
| ), | ||
| buildUserPersona( | ||
| receivedData.summaries, | ||
| sentData.summaries, | ||
| gmailSignature, | ||
| gmailTemplates, | ||
| ), | ||
| analyzeEmailBehavior(receivedData.summaries, sentData.summaries), | ||
| analyzeResponsePatterns(receivedData.summaries, sentData.summaries), | ||
| analyzeLabelOptimization(receivedData.summaries, gmailLabels), | ||
| ]); | ||
| console.log(`[${requestId}] AI analysis functions completed successfully`); | ||
|
|
||
| // Generate actionable recommendations based on all analysis | ||
| console.log(`[${requestId}] Generating actionable recommendations...`); | ||
| const actionableRecommendations = await generateActionableRecommendations( | ||
| receivedData.summaries, | ||
| userPersona, | ||
| emailBehavior, | ||
| ); | ||
| console.log(`[${requestId}] Actionable recommendations generated`); | ||
|
|
||
| // Compile comprehensive report | ||
| console.log(`[${requestId}] Compiling final report...`); | ||
| const comprehensiveReport = { | ||
| executiveSummary: { | ||
| ...executiveSummary, | ||
| keyMetrics: { | ||
| totalEmails: receivedData.totalEmails + sentData.totalEmails, | ||
| dateRange: `${totalDays} days (${oldestDate.toLocaleDateString()} - ${newestDate.toLocaleDateString()})`, | ||
| analysisFreshness: "Just now", | ||
| }, | ||
| }, | ||
| emailActivityOverview: { | ||
| dataSources: { | ||
| inbox: Math.floor(receivedData.totalEmails * 0.6), | ||
| archived: Math.floor(receivedData.totalEmails * 0.3), | ||
| trash: Math.floor(receivedData.totalEmails * 0.1), | ||
| sent: sentData.totalEmails, | ||
| }, | ||
| }, | ||
| userPersona, | ||
| emailBehavior, | ||
| responsePatterns, | ||
| labelAnalysis: { | ||
| currentLabels: gmailLabels.map((label) => ({ | ||
| name: label.name, | ||
| emailCount: label.messagesTotal || 0, | ||
| unreadCount: label.messagesUnread || 0, | ||
| })), | ||
| optimizationSuggestions: labelAnalysis.optimizationSuggestions, | ||
| }, | ||
| actionableRecommendations, | ||
| processingTime: Date.now() - startTime, | ||
| }; | ||
|
|
||
| console.log( | ||
| `[${requestId}] Analysis completed successfully in ${Date.now() - startTime}ms`, | ||
| ); | ||
| return NextResponse.json(comprehensiveReport); | ||
| } catch (error) { | ||
| const errorMessage = | ||
| error instanceof Error ? error.message : "Unknown error"; | ||
| const errorStack = error instanceof Error ? error.stack : undefined; | ||
|
|
||
| console.error(`[${requestId}] Comprehensive analysis error:`, { | ||
| error: errorMessage, | ||
| stack: errorStack, | ||
| processingTime: Date.now() - startTime, | ||
| url: request.url, | ||
| }); | ||
|
|
||
| return NextResponse.json( | ||
| { | ||
| error: "Analysis failed", | ||
| details: errorMessage, | ||
| requestId, | ||
| processingTime: Date.now() - startTime, | ||
| }, | ||
| { status: 500 }, | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Critical security vulnerability: Missing authentication middleware
This API route processes extensive email data and AI analysis without any authentication. This is a critical security vulnerability allowing unauthorized access to any user's email data.
Implement proper authentication:
+import { withEmailAccount } from "@/utils/api";
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("EmailAnalysis");
+
-export async function POST(request: NextRequest) {
+export const POST = withEmailAccount(async (request) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(7);
+ const emailAccountId = request.auth.emailAccountId;
- console.log(
- `[${requestId}] Starting comprehensive analysis for user: ${request.url}`,
- );
+ logger.with({ requestId }).info("Starting comprehensive analysis");
try {
- const body = await request.json();
- const { userEmail } = body;
-
- if (!userEmail) {
- console.log(`[${requestId}] Error: No userEmail provided`);
- return NextResponse.json(
- { error: "Email address is required" },
- { status: 400 },
- );
- }
// Fetch user's email account
- const emailAccount = await prisma.emailAccount.findFirst({
- where: { user: { email: userEmail } },
+ const emailAccount = await prisma.emailAccount.findUnique({
+ where: { id: emailAccountId },
include: { account: true },
});🏁 Script executed:
#!/bin/bash
# Search for withEmailAccount definition and usage
echo "Searching for withEmailAccount definition..."
rg "export (const|function) withEmailAccount" -n
echo -e "\nSearching for withEmailAccount usage in API routes..."
rg "withEmailAccount" -n apps/web/app
echo -e "\nSearching for withAuth usage in API routes..."
rg "withAuth" -n apps/web/app
echo -e "\nListing all route.ts files under apps/web/app..."
fd apps/web/app -t f -e ts | sed 's/^/ - /'Length of output: 13502
Enforce authentication with withEmailAccount middleware
This route handles user-specific email analysis and must be wrapped in withEmailAccount to prevent unauthorized access and scope all Prisma queries to the authenticated account. Replace raw console.log calls with a scoped logger and load the emailAccountId from request.auth.
• File to update: apps/web/app/(reports)/api/email-analysis/route.ts
• Wrap the handler, import from the existing middleware and logger utilities.
+ import { withEmailAccount } from "@/utils/middleware";
+ import { createScopedLogger } from "@/utils/logger";
- export async function POST(request: NextRequest) {
+ const logger = createScopedLogger("EmailAnalysis");
+ export const POST = withEmailAccount(async (request) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(7);
+ const emailAccountId = request.auth.emailAccountId;
- console.log(
- `[${requestId}] Starting comprehensive analysis for user: ${request.url}`,
- );
+ logger.with({ requestId }).info("Starting comprehensive analysis");
try {
- const body = await request.json();
- const { userEmail } = body;
-
- if (!userEmail) {
- console.log(`[${requestId}] Error: No userEmail provided`);
- return NextResponse.json(
- { error: "Email address is required" },
- { status: 400 },
- );
- }
// Fetch user's email account
- const emailAccount = await prisma.emailAccount.findFirst({
- where: { user: { email: userEmail } },
+ const emailAccount = await prisma.emailAccount.findUnique({
+ where: { id: emailAccountId },
include: { account: true },
});Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts from lines 768 to 1016,
the POST handler must be wrapped with the withEmailAccount middleware to enforce
authentication and scope Prisma queries to the authenticated user's email
account. Replace all raw console.log calls with the scoped logger imported from
the logger utilities. Also, obtain the emailAccountId from request.auth provided
by the middleware instead of fetching it manually. Import withEmailAccount and
the logger at the top, then export the wrapped handler as the default export.
| accessToken: emailAccount.account?.access_token ?? "", | ||
| refreshToken: emailAccount.account?.refresh_token ?? "", | ||
| expiresAt: emailAccount.account?.expires_at, | ||
| emailAccountId: emailAccount.id, | ||
| }); |
There was a problem hiding this comment.
Validate OAuth tokens before use
Using nullish coalescing with empty strings for OAuth tokens will cause authentication failures.
+ if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+ logger.with({ requestId }).error("OAuth tokens not found");
+ return NextResponse.json(
+ { error: "OAuth tokens not found" },
+ { status: 401 },
+ );
+ }
+
const gmail = await getGmailClientWithRefresh({
- accessToken: emailAccount.account?.access_token ?? "",
- refreshToken: emailAccount.account?.refresh_token ?? "",
+ accessToken: emailAccount.account.access_token,
+ refreshToken: emailAccount.account.refresh_token,
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts around lines 811 to 815,
the OAuth tokens are assigned using nullish coalescing with empty strings, which
can lead to authentication failures if tokens are missing or invalid. Instead of
defaulting to empty strings, add proper validation to check if access_token and
refresh_token exist and are valid before using them. If tokens are missing,
handle the error or trigger a token refresh flow rather than proceeding with
empty strings.
| console.log(`[${requestId}] Fetching email summaries...`); | ||
| const emailSummariesUrl = `${request.url.replace("/comprehensive-analysis", "/email-summaries")}`; | ||
|
|
||
| // Create AbortController for timeout handling | ||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 minute timeout | ||
|
|
||
| let receivedResponse: Response, sentResponse: Response; | ||
| try { | ||
| [receivedResponse, sentResponse] = await Promise.all([ | ||
| fetch(emailSummariesUrl, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ userEmail, count: 200 }), | ||
| signal: controller.signal, | ||
| }), | ||
| fetch(emailSummariesUrl, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ userEmail, query: "from:me", count: 50 }), | ||
| signal: controller.signal, | ||
| }), | ||
| ]); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid internal HTTP calls - use direct function calls
Making HTTP calls to internal API endpoints is inefficient and adds unnecessary network overhead.
Instead of making HTTP calls, extract the core logic into reusable functions:
// Extract summarization logic to a shared function
import { summarizeEmails } from "@/utils/email/summarize";
// Use the function directly
const [receivedSummaries, sentSummaries] = await Promise.all([
summarizeEmails({
gmail,
query: "",
count: 200,
emailAccountId
}),
summarizeEmails({
gmail,
query: "from:me",
count: 50,
emailAccountId
}),
]);🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts around lines 848 to 870,
the code makes internal HTTP POST requests to fetch email summaries, which is
inefficient. Refactor by extracting the email summarization logic into a
reusable function (e.g., summarizeEmails) and call this function directly with
appropriate parameters instead of making HTTP calls. Import the summarizeEmails
function and replace the fetch calls with direct calls to summarizeEmails,
passing gmail, query, count, and emailAccountId as needed.
| accessToken: emailAccount.account?.access_token!, | ||
| refreshToken: emailAccount.account?.refresh_token!, | ||
| expiresAt: emailAccount.account?.expires_at, | ||
| emailAccountId: emailAccount.id, | ||
| }); |
There was a problem hiding this comment.
Validate OAuth tokens before use
Using non-null assertions on nullable OAuth tokens can lead to runtime errors.
+ if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+ logger.with({ requestId }).error("OAuth tokens not found");
+ return NextResponse.json(
+ { error: "OAuth tokens not found" },
+ { status: 401 },
+ );
+ }
+
const gmail = await getGmailClientWithRefresh({
- accessToken: emailAccount.account?.access_token!,
- refreshToken: emailAccount.account?.refresh_token!,
+ accessToken: emailAccount.account.access_token,
+ refreshToken: emailAccount.account.refresh_token,
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| accessToken: emailAccount.account?.access_token!, | |
| refreshToken: emailAccount.account?.refresh_token!, | |
| expiresAt: emailAccount.account?.expires_at, | |
| emailAccountId: emailAccount.id, | |
| }); | |
| if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) { | |
| logger.with({ requestId }).error("OAuth tokens not found"); | |
| return NextResponse.json( | |
| { error: "OAuth tokens not found" }, | |
| { status: 401 }, | |
| ); | |
| } | |
| const gmail = await getGmailClientWithRefresh({ | |
| accessToken: emailAccount.account.access_token, | |
| refreshToken: emailAccount.account.refresh_token, | |
| expiresAt: emailAccount.account?.expires_at, | |
| emailAccountId: emailAccount.id, | |
| }); |
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-summaries/route.ts around lines 253 to 257,
the code uses non-null assertions on potentially nullable OAuth tokens, which
can cause runtime errors if the tokens are undefined or null. Modify the code to
explicitly check that access_token and refresh_token exist before using them,
and handle the case where they are missing by either throwing an error or
returning early to prevent unsafe usage.
| export async function POST(request: NextRequest) { | ||
| try { | ||
| const { userEmail } = await request.json(); | ||
|
|
||
| if (!userEmail) { | ||
| return NextResponse.json( | ||
| { error: "Email address is required" }, | ||
| { status: 400 }, | ||
| ); | ||
| } | ||
|
|
||
| const emailAccount = await prisma.emailAccount.findFirst({ | ||
| where: { user: { email: userEmail } }, | ||
| include: { account: true }, | ||
| }); | ||
|
|
||
| if (!emailAccount) { | ||
| return NextResponse.json( | ||
| { error: "Email account not found" }, | ||
| { status: 404 }, | ||
| ); | ||
| } | ||
|
|
||
| const gmail = await getGmailClientWithRefresh({ | ||
| accessToken: emailAccount.account.access_token!, | ||
| refreshToken: emailAccount.account.refresh_token!, | ||
| expiresAt: emailAccount.account.expires_at, | ||
| emailAccountId: emailAccount.id, | ||
| }); | ||
|
|
||
| const response = await gmail.users.labels.list({ userId: "me" }); | ||
|
|
||
| const userLabels = | ||
| response.data.labels?.filter( | ||
| (label: any) => | ||
| label.type === "user" && | ||
| !label.name.startsWith("CATEGORY_") && | ||
| !label.name.startsWith("CHAT"), | ||
| ) || []; | ||
|
|
||
| const labelsWithCounts = await Promise.all( | ||
| userLabels.map(async (label: any) => { | ||
| try { | ||
| const labelDetail = await gmail.users.labels.get({ | ||
| userId: "me", | ||
| id: label.id, | ||
| }); | ||
| return { | ||
| ...label, | ||
| messagesTotal: labelDetail.data.messagesTotal || 0, | ||
| messagesUnread: labelDetail.data.messagesUnread || 0, | ||
| threadsTotal: labelDetail.data.threadsTotal || 0, | ||
| threadsUnread: labelDetail.data.threadsUnread || 0, | ||
| }; | ||
| } catch (error) { | ||
| console.warn(`Failed to get details for label ${label.name}:`, error); | ||
| return { | ||
| ...label, | ||
| messagesTotal: 0, | ||
| messagesUnread: 0, | ||
| threadsTotal: 0, | ||
| threadsUnread: 0, | ||
| }; | ||
| } | ||
| }), | ||
| ); | ||
|
|
||
| const sortedLabels = labelsWithCounts.sort( | ||
| (a: any, b: any) => (b.messagesTotal || 0) - (a.messagesTotal || 0), | ||
| ); | ||
|
|
||
| return NextResponse.json({ | ||
| labels: sortedLabels.map((label: any) => ({ | ||
| id: label.id, | ||
| name: label.name, | ||
| messagesTotal: label.messagesTotal || 0, | ||
| messagesUnread: label.messagesUnread || 0, | ||
| color: label.color || null, | ||
| type: label.type, | ||
| })), | ||
| totalLabels: sortedLabels.length, | ||
| }); | ||
| } catch (error) { | ||
| console.error("Gmail labels fetch error:", error); | ||
| return NextResponse.json( | ||
| { error: "Failed to fetch Gmail labels" }, | ||
| { status: 500 }, | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Critical security vulnerability: Missing authentication and authorization middleware
This API route handles user data without any authentication or authorization. According to the coding guidelines, ALL API routes that handle user data MUST use appropriate authentication middleware (withAuth or withEmailAccount).
Additionally, this endpoint allows any user to fetch Gmail labels for any email account by simply providing an email address, which is a serious security vulnerability.
Consider refactoring this to:
- Use
withEmailAccountmiddleware since it operates on email account data - Remove the
userEmailparameter and use the authenticated user's email account - Consider using GET instead of POST for this read operation
-export async function POST(request: NextRequest) {
- try {
- const { userEmail } = await request.json();
-
- if (!userEmail) {
- return NextResponse.json(
- { error: "Email address is required" },
- { status: 400 },
- );
- }
-
- const emailAccount = await prisma.emailAccount.findFirst({
- where: { user: { email: userEmail } },
- include: { account: true },
- });
+import { withEmailAccount } from "@/utils/api";
+
+export const GET = withEmailAccount(async (request) => {
+ const emailAccountId = request.auth.emailAccountId;
+
+ const emailAccount = await prisma.emailAccount.findUnique({
+ where: { id: emailAccountId },
+ include: { account: true },
+ });🏁 Script executed:
#!/bin/bash
# Locate the Gmail labels route file
route_file=$(fd --hidden --follow --base-directory . -e ts "route.ts" | grep "gmail-labels/route.ts" || true)
echo "Found file: $route_file"
if [ -n "$route_file" ]; then
echo
echo "---- Checking for authentication middleware imports ----"
rg -n "withAuth|withEmailAccount" "$route_file" || echo "No authentication middleware imports found"
echo
echo "---- Checking exported handlers ----"
rg -n "export (const|async function) (GET|POST)" "$route_file" || echo "No GET/POST exports found"
fiLength of output: 905
Secure the Gmail labels route with withEmailAccount middleware
This endpoint currently allows any user to fetch labels by supplying an arbitrary email address and lacks any authentication/authorization guard, violating our API security guidelines. Please refactor as follows:
• File to update:
apps/web/app/(reports)/api/gmail-labels/route.ts
• Required changes:
- Import and wrap the handler with
withEmailAccount(notwithAuth) since this operates on a specific email account. - Remove the
userEmailrequest body parameter—userequest.auth.emailAccountIdinstead. - Change from
POSTtoGETfor this read-only operation.
Example diff:
-import { NextRequest, NextResponse } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
+import { withEmailAccount } from "@/utils/api";
-export async function POST(request: NextRequest) {
- const { userEmail } = await request.json();
- // … lookup by userEmail …
+export const GET = withEmailAccount(async (request) => {
+ const emailAccountId = request.auth.emailAccountId;
+ // … lookup by emailAccountId …🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/gmail-labels/route.ts lines 5 to 94, the POST
handler currently accepts a userEmail from the request body without
authentication, which is insecure. Refactor by changing the method from POST to
GET, remove the userEmail parameter from the request body, and instead use
request.auth.emailAccountId provided by the withEmailAccount middleware. Import
and wrap the handler with withEmailAccount to enforce authentication and
authorization based on the email account. Adjust the prisma query to find the
emailAccount by id using request.auth.emailAccountId rather than by user email.
| accessToken: emailAccount.account.access_token!, | ||
| refreshToken: emailAccount.account.refresh_token!, | ||
| expiresAt: emailAccount.account.expires_at, | ||
| emailAccountId: emailAccount.id, | ||
| }); |
There was a problem hiding this comment.
Avoid non-null assertions on nullable fields
Using non-null assertions on potentially nullable OAuth token fields is unsafe and could lead to runtime errors.
- const gmail = await getGmailClientWithRefresh({
- accessToken: emailAccount.account.access_token!,
- refreshToken: emailAccount.account.refresh_token!,
- expiresAt: emailAccount.account.expires_at,
- emailAccountId: emailAccount.id,
- });
+ if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+ return NextResponse.json(
+ { error: "OAuth tokens not found" },
+ { status: 401 },
+ );
+ }
+
+ const gmail = await getGmailClientWithRefresh({
+ accessToken: emailAccount.account.access_token,
+ refreshToken: emailAccount.account.refresh_token,
+ expiresAt: emailAccount.account.expires_at,
+ emailAccountId: emailAccount.id,
+ });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| accessToken: emailAccount.account.access_token!, | |
| refreshToken: emailAccount.account.refresh_token!, | |
| expiresAt: emailAccount.account.expires_at, | |
| emailAccountId: emailAccount.id, | |
| }); | |
| if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) { | |
| return NextResponse.json( | |
| { error: "OAuth tokens not found" }, | |
| { status: 401 }, | |
| ); | |
| } | |
| const gmail = await getGmailClientWithRefresh({ | |
| accessToken: emailAccount.account.access_token, | |
| refreshToken: emailAccount.account.refresh_token, | |
| expiresAt: emailAccount.account.expires_at, | |
| emailAccountId: emailAccount.id, | |
| }); |
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/gmail-labels/route.ts around lines 29 to 33,
remove the non-null assertion operators (!) from access_token and refresh_token
fields because these fields may be nullable. Instead, add proper null checks or
fallback handling before using these values to ensure the code safely handles
cases where tokens might be missing, preventing potential runtime errors.
| console.error("Error generating report:", error); | ||
| // You might want to show a toast notification here |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Show user-friendly error messages
Errors should be displayed to users, not just logged to console.
} catch (error) {
- console.error("Error generating report:", error);
- // You might want to show a toast notification here
+ toastError({
+ description: "Failed to generate report. Please try again."
+ });
} finally {📝 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.
| console.error("Error generating report:", error); | |
| // You might want to show a toast notification here | |
| } catch (error) { | |
| toastError({ | |
| description: "Failed to generate report. Please try again." | |
| }); | |
| } finally { |
🤖 Prompt for AI Agents
In apps/web/app/(reports)/email-analysis/page.tsx around lines 161 to 162, the
code only logs errors to the console without informing the user. Modify the
error handling to display a user-friendly error message, such as using a toast
notification or an alert component, so users are aware that an error occurred
during report generation.
apps/web/app/(reports)/layout.tsx
Outdated
| href="/sandbox" | ||
| className="flex items-center gap-2 hover:opacity-80 transition-opacity" |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Consider making the sandbox link dynamic
The hardcoded /sandbox link might not exist or might need to be configurable.
🏁 Script executed:
#!/bin/bash
# Check if /sandbox route exists in the app directory
fd -t f "page.tsx" apps/web/app | grep -E "sandbox/page.tsx|\\(reports\\)/page.tsx"
# Check for any sandbox-related routes
rg -l "sandbox" apps/web/app --type tsx --type ts | head -10Length of output: 170
🏁 Script executed:
#!/bin/bash
# 1. Check for a sandbox directory or page under the NextJS app folder
fd sandbox apps/web/app
# 2. Look for any literal '/sandbox' references across the repo
rg -n "/sandbox" .Length of output: 116
Action Required: Fix Broken Sandbox Link
A quick search confirms there is no apps/web/app/sandbox/page.tsx (or any other sandbox route) in the codebase, so the hardcoded href="/sandbox" will 404. Please address this by either:
- Adding a
sandboxpage underapps/web/app(e.g.apps/web/app/sandbox/page.tsx), - Removing or conditionally hiding the link until the sandbox feature is available,
- Extracting route paths into a shared constant (e.g.
src/constants/routes.ts) and using something likehref={ROUTES.sandbox}here to avoid hardcoded strings in the future.
Files to update:
- apps/web/app/(reports)/layout.tsx (line 108)
🤖 Prompt for AI Agents
In apps/web/app/(reports)/layout.tsx around lines 108 to 109, the href attribute
is set to "/sandbox" which leads to a 404 because the sandbox route does not
exist. Fix this by either creating a sandbox page at
apps/web/app/sandbox/page.tsx, or remove or conditionally hide the link until
the sandbox feature is implemented. Additionally, consider extracting route
paths into a shared constant file like src/constants/routes.ts and use that
constant for the href to avoid hardcoded strings.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (7)
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (3)
151-155: Remove unnecessary Content-Type header for GET requestThe Content-Type header is not needed for GET requests as they don't have a request body.
init: { method: "GET", - headers: { - "Content-Type": "application/json", - }, },
142-175: Add cleanup for fetch request on component unmountThe fetch request should be cancellable if the component unmounts before the request completes.
useEffect(() => { const generateReport = async () => { if (!emailAccountId) return; + const abortController = new AbortController(); try { const response = await fetchWithAccount({ url: "/api/email-report", emailAccountId, init: { method: "GET", headers: { "Content-Type": "application/json", }, + signal: abortController.signal, }, }); if (!response.ok) { throw new Error(`Failed to generate report: ${response.statusText}`); } const data = await response.json(); setReport(data); } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + // Request was cancelled, no need to update state + return; + } console.error("Error generating report:", error); setError( error instanceof Error ? error.message : "Failed to generate report", ); } finally { setIsLoading(false); } }; generateReport(); + + return () => { + abortController.abort(); + }; }, [emailAccountId]);
286-299: Consider using stable keys instead of array indicesUsing array indices as keys in React can cause issues with component state if items are reordered. Consider using unique identifiers where available.
For example, instead of:
{report.executiveSummary.quickActions .slice(0, 3) .map((action, index) => ( <div key={index} className="flex items-center gap-2">Consider using a combination of properties as key:
{report.executiveSummary.quickActions .slice(0, 3) .map((action, index) => ( <div key={`${action.action}-${action.difficulty}`} className="flex items-center gap-2">This applies to all the map functions throughout the component where index is used as the key.
Also applies to: 309-327, 349-360, 368-375, 426-433, 453-479, 486-512, 531-549, 558-585, 605-631, 640-657, 666-690
apps/web/app/(reports)/api/email-report/route.ts (1)
320-425: Consider parallelizing independent async operationsSeveral of the prompt generation functions could be run in parallel since they don't depend on each other's results.
// Run independent operations in parallel const [ executiveSummary, userPersona, emailBehavior, responsePatterns, labelAnalysis ] = await Promise.all([ generateExecutiveSummary( receivedEmailSummaries, sentEmailSummaries, gmailLabels, userEmail, emailAccount, ).catch(error => { logger.error("Failed to generate executive summary", { error: error instanceof Error ? error.message : String(error), emailAccountId, }); throw new Error( `Failed to generate executive summary: ${error instanceof Error ? error.message : String(error)}`, ); }), buildUserPersona( receivedEmailSummaries, userEmail, emailAccount, sentEmailSummaries, gmailSignature, gmailTemplates, ).catch(error => { logger.error("Failed to build user persona", { error: error instanceof Error ? error.message : String(error), emailAccountId, }); throw new Error( `Failed to build user persona: ${error instanceof Error ? error.message : String(error)}`, ); }), // ... similar for other functions ]); // Then generate actionableRecommendations which depends on userPersona and emailBehavior const actionableRecommendations = await generateActionableRecommendations( receivedEmailSummaries, userEmail, emailAccount, userPersona, emailBehavior, );This could significantly reduce the total execution time of the report generation.
apps/web/app/(reports)/api/email-report/fetch.ts (1)
15-16: Fix typo in documentationMinor typo in the comment.
- * This approach fetches one message at a time with retry and backofflogic, which is slower but more + * This approach fetches one message at a time with retry and backoff logic, which is slower but moreapps/web/app/(reports)/api/email-report/prompts.ts (2)
76-76: Add null check for label nameThe label name could potentially be null/undefined.
-${gmailLabels.map((label) => `- ${label.name} (${label.messagesTotal || 0} emails)`).join("\n")} +${gmailLabels.map((label) => `- ${label.name || 'Unnamed'} (${label.messagesTotal || 0} emails)`).join("\n")}
553-560: Consider propagating errors instead of silently returning empty arrayReturning an empty array on error could hide issues and make debugging difficult. Consider either propagating the error or implementing retry logic.
} catch (error) { logger.error("processEmailBatch: failed to summarize batch", { batchNumber, error, userEmail, }); - return []; + // Rethrow to let caller handle the error + throw new Error( + `Failed to summarize email batch ${batchNumber}: ${ + error instanceof Error ? error.message : String(error) + }` + ); }Alternatively, you could implement retry logic similar to what's done in the fetch module.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/[emailAccountId]/debug/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx(1 hunks)apps/web/app/(reports)/api/email-report/fetch.ts(1 hunks)apps/web/app/(reports)/api/email-report/prompts.ts(1 hunks)apps/web/app/(reports)/api/email-report/route.ts(1 hunks)apps/web/app/(reports)/api/email-report/schemas.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/web/app/(app)/[emailAccountId]/debug/page.tsx
- apps/web/app/(reports)/api/email-report/schemas.ts
🧰 Additional context used
📓 Path-based instructions (16)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
apps/web/**/*.tsx
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
apps/web/app/(app)/*/**
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts
**/*.{jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on 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 assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
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 a title element 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.
Assign tabIndex to non-interactive HTML elements with aria-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 a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using ZodFiles:
apps/web/app/(reports)/api/email-report/route.tsapps/web/app/(reports)/api/email-report/fetch.tsapps/web/app/(reports)/api/email-report/prompts.ts**/api/**/route.ts
📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...Files:
apps/web/app/(reports)/api/email-report/route.ts🧬 Code Graph Analysis (3)
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (2)
apps/web/utils/fetch.ts (1)
fetchWithAccount(7-25)apps/web/components/ui/card.tsx (4)
Card(137-137)CardHeader(138-138)CardTitle(140-140)CardContent(142-142)apps/web/app/(reports)/api/email-report/route.ts (7)
apps/web/utils/logger.ts (1)
createScopedLogger(17-65)apps/web/utils/middleware.ts (1)
withEmailAccount(190-194)apps/web/utils/types.ts (1)
ParsedMessage(47-58)apps/web/app/(reports)/api/email-report/fetch.ts (2)
fetchEmailsForReport(214-335)fetchGmailTemplates(386-415)apps/web/app/(reports)/api/email-report/schemas.ts (1)
EmailSummary(160-160)apps/web/app/(reports)/api/email-report/prompts.ts (7)
summarizeEmails(385-447)generateExecutiveSummary(19-121)buildUserPersona(123-184)analyzeEmailBehavior(186-231)analyzeResponsePatterns(233-292)analyzeLabelOptimization(294-339)generateActionableRecommendations(341-380)apps/web/utils/gmail/client.ts (1)
getGmailClientWithRefresh(49-98)apps/web/app/(reports)/api/email-report/prompts.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-65)apps/web/app/(reports)/api/email-report/schemas.ts (7)
EmailSummary(160-160)executiveSummarySchema(12-51)userPersonaSchema(53-63)emailBehaviorSchema(65-82)responsePatternsSchema(84-118)labelAnalysisSchema(120-131)actionableRecommendationsSchema(133-158)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)apps/web/utils/types.ts (1)
ParsedMessage(47-58)⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
| emailAccount = await prisma.emailAccount.findFirst({ | ||
| where: { user: { email: userEmail } }, | ||
| include: { | ||
| account: true, | ||
| user: { | ||
| select: { | ||
| email: true, | ||
| aiProvider: true, | ||
| aiModel: true, | ||
| aiApiKey: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
There was a problem hiding this comment.
Add ownership validation for emailAccountId
The current query only filters by user email but doesn't validate that the provided emailAccountId belongs to that user. This could potentially allow access to other users' email accounts.
emailAccount = await prisma.emailAccount.findFirst({
- where: { user: { email: userEmail } },
+ where: {
+ id: emailAccountId,
+ user: { email: userEmail }
+ },
include: {
account: true,
user: {
select: {
email: true,
aiProvider: true,
aiModel: true,
aiApiKey: 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.
| emailAccount = await prisma.emailAccount.findFirst({ | |
| where: { user: { email: userEmail } }, | |
| include: { | |
| account: true, | |
| user: { | |
| select: { | |
| email: true, | |
| aiProvider: true, | |
| aiModel: true, | |
| aiApiKey: true, | |
| }, | |
| }, | |
| }, | |
| }); | |
| emailAccount = await prisma.emailAccount.findFirst({ | |
| where: { | |
| id: emailAccountId, | |
| user: { email: userEmail }, | |
| }, | |
| include: { | |
| account: true, | |
| user: { | |
| select: { | |
| email: true, | |
| aiProvider: true, | |
| aiModel: true, | |
| aiApiKey: true, | |
| }, | |
| }, | |
| }, | |
| }); |
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/route.ts around lines 180 to 194, the
query fetching emailAccount only filters by user email and does not verify that
the provided emailAccountId belongs to that user. To fix this, update the
query's where clause to include a condition that matches the emailAccountId with
the provided ID and ensure it belongs to the user with the given email. This
will enforce ownership validation and prevent unauthorized access to other
users' email accounts.
| let executiveSummary: any, | ||
| userPersona: any, | ||
| emailBehavior: any, | ||
| responsePatterns: any, | ||
| labelAnalysis: any, | ||
| actionableRecommendations: any; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Replace 'any' types with proper type imports
Using 'any' type defeats TypeScript's type safety benefits.
- let executiveSummary: any,
- userPersona: any,
- emailBehavior: any,
- responsePatterns: any,
- labelAnalysis: any,
- actionableRecommendations: any;
+ let executiveSummary: Awaited<ReturnType<typeof generateExecutiveSummary>>,
+ userPersona: Awaited<ReturnType<typeof buildUserPersona>>,
+ emailBehavior: Awaited<ReturnType<typeof analyzeEmailBehavior>>,
+ responsePatterns: Awaited<ReturnType<typeof analyzeResponsePatterns>>,
+ labelAnalysis: Awaited<ReturnType<typeof analyzeLabelOptimization>>,
+ actionableRecommendations: Awaited<ReturnType<typeof generateActionableRecommendations>>;Or better yet, import the schema types:
import type { z } from "zod";
import type {
executiveSummarySchema,
userPersonaSchema,
emailBehaviorSchema,
responsePatternsSchema,
labelAnalysisSchema,
actionableRecommendationsSchema
} from "./schemas";
// Then use:
let executiveSummary: z.infer<typeof executiveSummarySchema>;
// etc.🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/route.ts around lines 313 to 318, the
variables executiveSummary, userPersona, emailBehavior, responsePatterns,
labelAnalysis, and actionableRecommendations are all typed as 'any', which
bypasses TypeScript's type safety. To fix this, import the relevant schema types
from the schemas file and use z.infer<typeof schema> to type each variable
accordingly, replacing 'any' with the inferred types from the respective
schemas.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/web/app/(reports)/api/email-report/fetch.ts (3)
204-306: Solid implementation with minor improvements needed.The function is well-structured with proper error handling and Gmail client management. Consider these small improvements:
+const API_RATE_LIMIT_DELAY = 3000; // 3 seconds between API calls + export async function fetchEmailsForReport({ emailAccount, }: { emailAccount: EmailAccount & { account: Account; user: Pick<User, "email" | "aiProvider" | "aiModel" | "aiApiKey">; }; }): Promise<EmailFetchResult> { // ... existing code ... - await new Promise((resolve) => setTimeout(resolve, 3000)); + await new Promise((resolve) => setTimeout(resolve, API_RATE_LIMIT_DELAY));Also consider making the target counts (200, 50) configurable constants for better maintainability.
357-386: Good implementation with minor improvements suggested.The function handles draft processing well with proper error isolation. Consider extracting the magic numbers as constants for better maintainability.
+const MAX_DRAFTS_TO_FETCH = 50; +const MAX_TEMPLATES_TO_RETURN = 10; + export async function fetchGmailTemplates( gmail: gmail_v1.Gmail, ): Promise<string[]> { try { - const drafts = await fetchEmailsByQuery(gmail, "in:draft", 50); + const drafts = await fetchEmailsByQuery(gmail, "in:draft", MAX_DRAFTS_TO_FETCH); const templates: string[] = []; for (const draft of drafts) { try { if (draft.textPlain?.trim()) { templates.push(draft.textPlain.trim()); } - if (templates.length >= 10) break; + if (templates.length >= MAX_TEMPLATES_TO_RETURN) break; } catch (error) {
1-387: Overall solid implementation with good practices.This module demonstrates good TypeScript practices, comprehensive error handling, and proper Gmail API integration. The extensive logging will be valuable for debugging production issues. The main areas for improvement are function complexity and magic number usage, but the code is production-ready.
Consider creating a constants file or configuration object for the various numeric values used throughout (retry counts, delays, batch sizes) to improve maintainability.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/app/(reports)/api/email-report/fetch.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(reports)/api/email-report/fetch.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(reports)/api/email-report/fetch.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). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (4)
apps/web/app/(reports)/api/email-report/fetch.ts (4)
1-10: LGTM! Proper imports and logger setup.The imports follow TypeScript best practices with
import typefor type-only imports, correct path aliases, and proper logger initialization at the module level as specified in the coding guidelines.
197-202: Well-structured interface definition.Clear and properly typed interface that effectively defines the return structure for email fetching operations.
308-339: Clean implementation with good source organization.The function effectively handles multiple email sources with proper error isolation. The configuration-driven approach makes it easy to maintain and extend.
341-355: Simple and effective implementation.Good error handling with graceful degradation, returning an empty array instead of throwing when sent emails can't be fetched.
| async function fetchEmailsByQuery( | ||
| gmail: gmail_v1.Gmail, | ||
| query: string, | ||
| count: number, | ||
| ): Promise<ParsedMessage[]> { | ||
| const emails: ParsedMessage[] = []; | ||
| let nextPageToken: string | undefined; | ||
| let retryCount = 0; | ||
| const maxRetries = 3; | ||
|
|
||
| logger.info("fetchEmailsByQuery started", { | ||
| query, | ||
| targetCount: count, | ||
| maxRetries, | ||
| }); | ||
|
|
||
| while (emails.length < count && retryCount < maxRetries) { | ||
| try { | ||
| logger.info("fetchEmailsByQuery: calling getMessages", { | ||
| query, | ||
| maxResults: Math.min(100, count - emails.length), | ||
| hasPageToken: !!nextPageToken, | ||
| currentEmailsCount: emails.length, | ||
| retryCount, | ||
| }); | ||
|
|
||
| const response = await getMessages(gmail, { | ||
| query: query || undefined, | ||
| maxResults: Math.min(100, count - emails.length), | ||
| pageToken: nextPageToken, | ||
| }); | ||
|
|
||
| logger.info("fetchEmailsByQuery: getMessages response received", { | ||
| hasMessages: !!response.messages, | ||
| messagesCount: response.messages?.length || 0, | ||
| hasNextPageToken: !!response.nextPageToken, | ||
| responseKeys: Object.keys(response), | ||
| }); | ||
|
|
||
| if (!response.messages || response.messages.length === 0) { | ||
| logger.info("fetchEmailsByQuery: no messages found, breaking"); | ||
| break; | ||
| } | ||
|
|
||
| logger.info("fetchEmailsByQuery: starting to fetch individual messages", { | ||
| messageIdsCount: response.messages?.length || 0, | ||
| messageIds: response.messages?.map((m: any) => m.id).slice(0, 5) || [], | ||
| }); | ||
|
|
||
| const messagePromises = (response.messages || []).map( | ||
| async (message: any, index: number) => { | ||
| if (!message.id) { | ||
| logger.warn("fetchEmailsByQuery: message without ID", { | ||
| index, | ||
| message, | ||
| }); | ||
| return null; | ||
| } | ||
|
|
||
| logger.info("fetchEmailsByQuery: fetching individual message", { | ||
| messageId: message.id, | ||
| index, | ||
| totalMessages: response.messages?.length || 0, | ||
| }); | ||
|
|
||
| for (let i = 0; i < 3; i++) { | ||
| try { | ||
| logger.info("fetchEmailsByQuery: calling getMessage", { | ||
| messageId: message.id, | ||
| attempt: i + 1, | ||
| format: "full", | ||
| }); | ||
|
|
||
| const messageWithPayload = await getMessage( | ||
| message.id, | ||
| gmail, | ||
| "full", | ||
| ); | ||
|
|
||
| logger.info("fetchEmailsByQuery: getMessage successful", { | ||
| messageId: message.id, | ||
| hasPayload: !!messageWithPayload, | ||
| payloadKeys: messageWithPayload | ||
| ? Object.keys(messageWithPayload) | ||
| : [], | ||
| }); | ||
|
|
||
| const parsedMessage = parseMessage(messageWithPayload); | ||
| logger.info("fetchEmailsByQuery: message parsed successfully", { | ||
| messageId: message.id, | ||
| hasHeaders: !!parsedMessage.headers, | ||
| hasTextPlain: !!parsedMessage.textPlain, | ||
| hasTextHtml: !!parsedMessage.textHtml, | ||
| }); | ||
|
|
||
| return parsedMessage; | ||
| } catch (error) { | ||
| logger.warn("fetchEmailsByQuery: getMessage attempt failed", { | ||
| error, | ||
| messageId: message.id, | ||
| attempt: i + 1, | ||
| }); | ||
|
|
||
| if (i === 2) { | ||
| logger.warn( | ||
| `Failed to fetch message ${message.id} after 3 attempts:`, | ||
| { error }, | ||
| ); | ||
| return null; | ||
| } | ||
| await new Promise((resolve) => | ||
| setTimeout(resolve, 1000 * (i + 1)), | ||
| ); | ||
| } | ||
| } | ||
| return null; | ||
| }, | ||
| ); | ||
|
|
||
| logger.info("fetchEmailsByQuery: waiting for all message promises", { | ||
| promisesCount: messagePromises.length, | ||
| }); | ||
|
|
||
| const messages = await Promise.all(messagePromises); | ||
| const validMessages = messages.filter((msg) => msg !== null); | ||
|
|
||
| logger.info("fetchEmailsByQuery: message promises completed", { | ||
| totalMessages: messages.length, | ||
| validMessages: validMessages.length, | ||
| nullMessages: messages.length - validMessages.length, | ||
| }); | ||
|
|
||
| emails.push(...validMessages); | ||
|
|
||
| nextPageToken = response.nextPageToken || undefined; | ||
| if (!nextPageToken) { | ||
| logger.info("fetchEmailsByQuery: no next page token, breaking"); | ||
| break; | ||
| } | ||
|
|
||
| retryCount = 0; | ||
| logger.info("fetchEmailsByQuery: successful iteration completed", { | ||
| currentEmailsCount: emails.length, | ||
| targetCount: count, | ||
| hasNextPageToken: !!nextPageToken, | ||
| }); | ||
| } catch (error) { | ||
| retryCount++; | ||
| logger.error("fetchEmailsByQuery: main loop error", { | ||
| error, | ||
| retryCount, | ||
| maxRetries, | ||
| currentEmailsCount: emails.length, | ||
| targetCount: count, | ||
| }); | ||
|
|
||
| if (retryCount >= maxRetries) { | ||
| logger.error(`Failed to fetch emails after ${maxRetries} attempts:`, { | ||
| error, | ||
| }); | ||
| break; | ||
| } | ||
|
|
||
| await new Promise((resolve) => setTimeout(resolve, 2000 * retryCount)); | ||
| } | ||
| } | ||
|
|
||
| logger.info("fetchEmailsByQuery completed", { | ||
| finalEmailsCount: emails.length, | ||
| targetCount: count, | ||
| finalRetryCount: retryCount, | ||
| }); | ||
|
|
||
| return emails; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Refactor this large function into smaller, focused functions.
This function is quite complex and handles multiple responsibilities (pagination, retry logic, message fetching, parsing). Consider breaking it down into smaller functions for better maintainability and testability.
Consider extracting these responsibilities:
+// Extract constants
+const MAX_RESULTS_PER_PAGE = 100;
+const MAX_MESSAGE_RETRIES = 3;
+const RETRY_BASE_DELAY = 1000;
+const MAIN_RETRY_BASE_DELAY = 2000;
+
+async function fetchMessageWithRetry(messageId: string, gmail: gmail_v1.Gmail): Promise<ParsedMessage | null> {
+ for (let i = 0; i < MAX_MESSAGE_RETRIES; i++) {
+ try {
+ const messageWithPayload = await getMessage(messageId, gmail, "full");
+ return parseMessage(messageWithPayload);
+ } catch (error) {
+ logger.warn("getMessage attempt failed", { error, messageId, attempt: i + 1 });
+ if (i === MAX_MESSAGE_RETRIES - 1) return null;
+ await new Promise(resolve => setTimeout(resolve, RETRY_BASE_DELAY * (i + 1)));
+ }
+ }
+ return null;
+}
+
+async function fetchMessagesPage(gmail: gmail_v1.Gmail, query: string, maxResults: number, pageToken?: string) {
+ const response = await getMessages(gmail, {
+ query: query || undefined,
+ maxResults,
+ pageToken,
+ });
+
+ if (!response.messages?.length) return { messages: [], nextPageToken: undefined };
+
+ const messagePromises = response.messages.map(message =>
+ message.id ? fetchMessageWithRetry(message.id, gmail) : Promise.resolve(null)
+ );
+
+ const messages = await Promise.all(messagePromises);
+ return {
+ messages: messages.filter(msg => msg !== null),
+ nextPageToken: response.nextPageToken
+ };
+}This would make the main function much more readable and each piece easier to test.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/fetch.ts between lines 21 and 195,
the fetchEmailsByQuery function is too large and handles multiple
responsibilities like pagination, retry logic, fetching individual messages, and
parsing. Refactor by extracting smaller focused functions for each
responsibility: one for handling pagination and retry logic, one for fetching
and retrying individual messages, and one for parsing messages. Then, have the
main function orchestrate these smaller functions to improve readability and
maintainability.
There was a problem hiding this comment.
Actionable comments posted: 2
🔭 Outside diff range comments (1)
.cursor/rules/llm.mdc (1)
50-82: Fix incomplete parameter renamingThe parameter has been renamed from
usertoemailAccount, but the destructuring on line 52 and the reference on line 81 haven't been updated. This will cause runtime errors.export async function featureFunction(options: { inputData: InputType; emailAccount: EmailAccountWithAI; }) { - const { inputData, user } = options; + const { inputData, emailAccount } = options; // ... other code ... const result = await chatCompletionObject({ userAi: emailAccount.user, system, prompt, schema, - userEmail: user.email, + userEmail: emailAccount.email, usageLabel: "Feature Name", });
🧹 Nitpick comments (2)
apps/web/utils/ai/report/fetch.ts (1)
11-20: Fix typos in documentationThe documentation contains several typos that should be corrected for clarity.
- * Uses sequential message fetching instead of batch loads to avoid Gmail API rate limits. - * This approach fetches one message at a time with retry and backofflogic, which is slower but more - * reliable than trying to fetch 100 messages at once. - * - * Not usinggetMessagesLargeBatch because it expects the messageIds - * queryBatchMessages is limited to 20 messages at a time + * Uses sequential message fetching instead of batch loads to avoid Gmail API rate limits. + * This approach fetches one message at a time with retry and backoff logic, which is slower but more + * reliable than trying to fetch 100 messages at once. + * + * Not using getMessagesLargeBatch because it expects the messageIds + * queryBatchMessages is limited to 20 messages at a timeapps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (1)
195-203: Remove duplicate "Professional Identity" headingThe card already has "Professional Identity" as its title, so the h4 with the same text is redundant.
<CardHeader> <CardTitle className="flex items-center gap-2"> <TrendingUp className="h-5 w-5" /> Professional Identity </CardTitle> </CardHeader> <CardContent className="space-y-6"> <div> - <h4 className="font-semibold text-gray-900 mb-3"> - Professional Identity - </h4> <p className="text-lg font-medium text-blue-600 mb-2"> {report.userPersona.professionalIdentity.persona} </p>
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.cursor/rules/llm.mdc(2 hunks)apps/web/app/(app)/[emailAccountId]/debug/page.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx(1 hunks)apps/web/app/(landing)/components/test-action.ts(1 hunks)apps/web/utils/actions/report.ts(1 hunks)apps/web/utils/ai/report/fetch.ts(1 hunks)apps/web/utils/ai/report/prompts.ts(1 hunks)apps/web/utils/gmail/message.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/web/utils/gmail/message.ts
- apps/web/app/(landing)/components/test-action.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/[emailAccountId]/debug/page.tsx
🧰 Additional context used
📓 Path-based instructions (22)
.cursor/rules/*.mdc
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule
Files:
.cursor/rules/llm.mdc
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
.cursor/rules/llm.mdcapps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
.cursor/rules/llm.mdcapps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/**/*.tsx
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts
apps/web/app/(app)/*/**
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts
**/*.{jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on 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 assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
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 a title element 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.
Assign tabIndex to non-interactive HTML elements with aria-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 a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsxapps/web/utils/actions/**/*.ts
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.Files:
apps/web/utils/actions/report.ts**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using ZodFiles:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.tsapps/web/utils/actions/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject in the.action()handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use serverFiles:
apps/web/utils/actions/report.tsapps/web/utils/**
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logicFiles:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.tsapps/web/utils/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle sizeFiles:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.tsapps/web/utils/{ai,llms}/**/*
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.Files:
apps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.tsapps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.Files:
apps/web/utils/ai/report/fetch.tsapps/web/utils/ai/report/prompts.ts🧠 Learnings (6)
📓 Common learnings
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/index.mdc:0-0 Timestamp: 2025-07-18T15:05:56.644Z Learning: Guidelines for working with Gmail APILearnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/utilities.mdc:0-0 Timestamp: 2025-07-19T17:50:28.270Z Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests..cursor/rules/llm.mdc (16)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Run AI tests with: pnpm test-ai your-featureLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-07-18T15:07:12.415Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use kebab-case for feature flag keys (e.g., 'inbox-cleaner', 'pricing-options-2')Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Log inputs and outputs with appropriate log levels in LLM-related functions.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Remove excessive whitespace and truncate long inputs in LLM prompts.apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (23)
Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at:apps/web/app/(app)/PAGE_NAME/page.tsxLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsxLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loadingLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either inpage.tsxor in theapps/web/app/(app)/PAGE_NAMEfolderLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folderLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/tests/**/*.test.ts : Use descriptive console.debug statements for generated content in LLM-related testsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Use proper error handling with try/catch blocksLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use descriptive scoped loggers for each LLM feature.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : UseLoadingContentcomponent to handle loading and error states consistentlyLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: UI component and styling guidelines (Shadcn, Tailwind)Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use theLoadingContentcomponent to handle loading and error states consistently in data-fetching components.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async dataLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Implement responsive design with Tailwind CSS using a mobile-first approachapps/web/utils/actions/report.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: Theutilsfolder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start withuse serverLearnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.apps/web/utils/ai/report/fetch.ts (9)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : UsewithEmailAccountfor email-account-level operationsLearnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: Theutilsfolder also contains core app logic such as Next.js Server Actions and Gmail API requests.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for working with Gmail APILearnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.apps/web/utils/ai/report/prompts.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.🧬 Code Graph Analysis (1)
apps/web/utils/ai/report/fetch.ts (5)
apps/web/utils/logger.ts (1)
createScopedLogger(17-65)apps/web/utils/types.ts (1)
ParsedMessage(55-70)apps/web/utils/gmail/message.ts (3)
getMessages(252-270)getMessage(66-78)parseMessage(22-31)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/account.ts (1)
getGmailClientForEmail(14-27)⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (19)
apps/web/utils/ai/report/fetch.ts (6)
1-10: LGTM!Imports are properly organized and follow project conventions. The scoped logger is correctly initialized.
21-195: Well-implemented email fetching with robust error handlingThe function has excellent retry logic, comprehensive logging, and handles Gmail API rate limits gracefully. The parallel fetching of messages within each page while maintaining sequential page fetching is a good balance between performance and reliability.
197-232: Clean implementation with proper rate limitingThe interface is well-defined and the function properly handles fetching both received and sent emails with appropriate delays to avoid rate limiting.
234-265: Smart email source prioritizationGood implementation that fetches from multiple sources (inbox, archived, trash) without duplicates. The error handling ensures resilience when one source fails.
267-281: LGTM!Simple and effective implementation for fetching sent emails with proper error handling.
283-312: Robust template extraction with proper error handlingGood implementation that gracefully handles errors at both the individual draft and overall function levels. The template limit of 10 is reasonable.
apps/web/utils/actions/report.ts (4)
1-26: Excellent server action setupThe file correctly implements all server action requirements: "use server" directive, proper use of actionClient, and well-organized imports. The EmailReportData type export is helpful for client components.
27-32: LGTM!The action correctly uses actionClient with emailAccountId from context, following the project's server action patterns.
34-96: Robust label fetching with excellent error handlingThe function properly filters system labels, fetches detailed counts, and handles errors gracefully at multiple levels. The sorting by message count is a nice touch for report presentation.
98-133: LGTM!Proper implementation for fetching Gmail signature with appropriate null checks and error handling.
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (2)
53-589: Excellent UI implementationThe component has a well-structured, comprehensive UI that displays all report sections with proper visual hierarchy, responsive layouts, and appropriate use of badges for categorization. Good use of LoadingContent for state management.
591-622: LGTM!Clean helper functions with intuitive color coding for badges. The exhaustive switch statements ensure all cases are handled.
apps/web/utils/ai/report/prompts.ts (7)
1-164: Excellent schema definitions following LLM best practicesThe schemas are comprehensive, well-documented with
describe()calls, and specific enough to guide LLM output effectively. The setup follows all the recommended patterns.
166-264: Excellent implementation following LLM guidelinesThe function perfectly follows the LLM pattern with separated prompts, proper logging, and schema validation. The detailed persona examples and identification instructions will help generate accurate professional personas.
266-323: LGTM!Good implementation with proper handling of optional data and clear prompt structure.
325-423: Excellent prompt engineering with clear category guidelinesBoth functions follow LLM patterns correctly. The detailed instructions in
analyzeResponsePatternsabout avoiding meaningless categories (like "Other", "Miscellaneous") and providing good category examples is excellent prompt engineering.
425-502: LGTM!Both functions properly implement the LLM pattern with context-aware prompts that will generate practical, actionable recommendations.
504-563: Smart batching implementationExcellent implementation with batching to handle large email volumes and delays between batches to avoid rate limits. Comprehensive logging helps with debugging.
565-661: Robust batch processing with excellent loggingThe function handles email batches efficiently with content truncation, detailed logging at each step, and clear summarization rules. The logging of AI provider details is particularly helpful for debugging.
| const { executeAsync, isExecuting, result } = useAction( | ||
| generateReportAction.bind(null, emailAccountId), | ||
| { | ||
| onSuccess: () => { | ||
| if (result?.data) { | ||
| setReport(result.data); | ||
| toastSuccess({ description: "Report generated successfully" }); | ||
| } else { | ||
| toastError({ description: "Failed to generate report" }); | ||
| } | ||
| }, | ||
| onError: (result) => { | ||
| toastError({ | ||
| title: "Failed to generate report", | ||
| description: result.error.serverError || "Unknown error", | ||
| }); | ||
| }, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Fix result access in onSuccess handler
The onSuccess handler is accessing result from the outer scope instead of using the callback parameter. This could lead to stale data.
const { executeAsync, isExecuting, result } = useAction(
generateReportAction.bind(null, emailAccountId),
{
- onSuccess: () => {
- if (result?.data) {
- setReport(result.data);
+ onSuccess: (result) => {
+ if (result.data) {
+ setReport(result.data);
toastSuccess({ description: "Report generated successfully" });
} else {
toastError({ description: "Failed to generate report" });
}
},
onError: (result) => {
toastError({
title: "Failed to generate report",
description: result.error.serverError || "Unknown error",
});
},
},
);📝 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.
| const { executeAsync, isExecuting, result } = useAction( | |
| generateReportAction.bind(null, emailAccountId), | |
| { | |
| onSuccess: () => { | |
| if (result?.data) { | |
| setReport(result.data); | |
| toastSuccess({ description: "Report generated successfully" }); | |
| } else { | |
| toastError({ description: "Failed to generate report" }); | |
| } | |
| }, | |
| onError: (result) => { | |
| toastError({ | |
| title: "Failed to generate report", | |
| description: result.error.serverError || "Unknown error", | |
| }); | |
| }, | |
| }, | |
| ); | |
| const { executeAsync, isExecuting, result } = useAction( | |
| generateReportAction.bind(null, emailAccountId), | |
| { | |
| onSuccess: (result) => { | |
| if (result.data) { | |
| setReport(result.data); | |
| toastSuccess({ description: "Report generated successfully" }); | |
| } else { | |
| toastError({ description: "Failed to generate report" }); | |
| } | |
| }, | |
| onError: (result) => { | |
| toastError({ | |
| title: "Failed to generate report", | |
| description: result.error.serverError || "Unknown error", | |
| }); | |
| }, | |
| }, | |
| ); |
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx around lines 33 to
51, the onSuccess handler incorrectly accesses the outer scoped result variable
instead of using the result parameter passed to the callback. To fix this,
update the onSuccess handler to accept a parameter (e.g., response) and use this
parameter to access the data instead of the outer result variable, ensuring
fresh and accurate data is used.
apps/web/utils/actions/report.ts
Outdated
| executiveSummary, | ||
| emailActivityOverview: { | ||
| dataSources: { | ||
| inbox: totalReceived, | ||
| archived: 0, | ||
| trash: 0, | ||
| sent: totalSent, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Update data sources to reflect actual email fetching
The dataSources object shows 0 for archived and trash, but fetchReceivedEmails actually fetches from these sources. Consider tracking the actual counts from each source for accuracy.
The current implementation in fetchReceivedEmails fetches from inbox, archived, and trash but doesn't return counts per source. You might want to refactor fetchEmailsForReport and fetchReceivedEmails to return source-specific counts for accurate reporting.
🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts around lines 204 to 212, the dataSources
object sets archived and trash counts to zero, which is inaccurate since
fetchReceivedEmails fetches emails from these sources. Refactor
fetchReceivedEmails and fetchEmailsForReport to return counts of emails fetched
from each source (inbox, archived, trash) separately, then update the
dataSources object to use these actual counts instead of zeros for archived and
trash to ensure accurate reporting.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/web/utils/actions/report.ts (1)
225-234: Update data sources to reflect actual email fetchingThe
dataSourcesobject shows 0 for archived and trash, butfetchReceivedEmailsactually fetches from these sources. Consider tracking the actual counts from each source for accuracy.
🧹 Nitpick comments (1)
apps/web/utils/ai/report/prompts.ts (1)
544-586: Consider adding error handling for individual batch failures.While the function follows LLM best practices, consider adding error handling around the
chatCompletionObjectcall to prevent entire batch failures from stopping the summarization process.const result = await chatCompletionObject({ userAi: emailAccount.user, system, prompt, schema: z.array(emailSummarySchema), userEmail: emailAccount.email, usageLabel: "email-report-summary-generation", - }); + }).catch((error) => { + logger.error("Failed to process email batch", { + batchNumber, + totalBatches, + error: error instanceof Error ? error.message : String(error) + }); + // Return empty array or partial results based on your needs + return { object: [] }; + });
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx(1 hunks)apps/web/utils/actions/report.ts(1 hunks)apps/web/utils/ai/report/fetch.ts(1 hunks)apps/web/utils/ai/report/prompts.ts(1 hunks)apps/web/utils/get-email-from-message.ts(1 hunks)apps/web/utils/types.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/web/utils/get-email-from-message.ts
- apps/web/utils/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/utils/ai/report/fetch.ts
- apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.
Files:
apps/web/utils/actions/report.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
apps/web/utils/actions/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject in the.action()handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use server
Files:
apps/web/utils/actions/report.ts
apps/web/utils/**
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
apps/web/utils/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/actions/report.tsapps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.
Files:
apps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.
Files:
apps/web/utils/ai/report/prompts.ts
🧠 Learnings (2)
apps/web/utils/actions/report.ts (16)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start with use server
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement early returns for invalid inputs in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
apps/web/utils/ai/report/prompts.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (13)
apps/web/utils/actions/report.ts (5)
28-33: LGTM! Server action follows best practices.The action correctly uses
actionClientfor email account context, includes metadata for monitoring, and has proper schema validation. The intensive AI report generation justifies using a server action despite being primarily a data operation.
35-97: Excellent error handling and performance optimization.The function demonstrates solid practices:
- Concurrent fetching with
Promise.allfor performance- Proper error handling at both function and individual label levels
- Type guards ensure data integrity
- Graceful degradation with fallback values on API failures
99-134: Solid implementation with proper error handling.The function correctly handles the Gmail sendAs API flow with appropriate fallbacks and informative logging.
170-213: Excellent concurrent processing with robust error handling.The Promise.all implementation with individual .catch() handlers ensures that failures in one AI function don't break the entire report generation. This approach maximizes data availability while maintaining resilience.
215-223: Smart conditional logic for dependent recommendations.The conditional generation of actionable recommendations based on successful user persona generation is a good design pattern that prevents unnecessary AI calls when prerequisites are missing.
apps/web/utils/ai/report/prompts.ts (8)
12-168: Excellent schema design for structured AI outputs.The Zod schemas are comprehensive and well-designed:
- Detailed descriptions guide LLM output effectively
- Appropriate use of enums, constraints, and nested objects
- Schemas are specific enough to ensure consistent AI responses
This follows LLM best practices for output validation and structure.
170-268: Exemplary LLM function structure and prompt engineering.This function demonstrates excellent adherence to LLM best practices:
- Clear separation of system prompt (role definition) and user prompt (data)
- Comprehensive prompt engineering with good/bad examples
- Proper use of
chatCompletionObjectwith schema validation- Appropriate usage labeling for monitoring
The detailed persona identification logic with confidence scoring is particularly well-designed.
270-327: Clean implementation with good optional parameter handling.The function properly handles optional parameters (sentEmailSummaries, gmailSignature, gmailTemplates) using conditional template strings, and maintains the standard LLM structure.
329-370: Standard LLM implementation with clear focus on actionable insights.The function follows established patterns and maintains focus on automation opportunities and actionable patterns, which aligns well with the report's practical objectives.
372-427: Thoughtful prompt engineering prevents generic categorization.The explicit guidance against meaningless categories like "Other", "Miscellaneous", and "Personal" with clear examples of good vs bad categories demonstrates excellent prompt engineering. This will lead to more actionable email organization suggestions.
429-470: Well-structured label optimization analysis.The function effectively combines current label data with email content patterns to provide actionable optimization suggestions. The focus on practical time-saving improvements is appropriate.
472-506: Smart dependency on previous analysis results.The function effectively leverages the user persona analysis to generate targeted recommendations, demonstrating good architectural design where AI functions build upon each other's outputs.
511-542: Solid batching implementation with rate limiting.The batching logic is well-implemented with appropriate batch sizes, sleep intervals for rate limiting, and clear progress tracking. The early return for empty arrays prevents unnecessary processing.
There was a problem hiding this comment.
Actionable comments posted: 12
♻️ Duplicate comments (1)
apps/web/utils/actions/report.ts (1)
127-132: Update data sources to reflect actual email fetchingThe
dataSourcesobject shows 0 for archived and trash, butfetchReceivedEmailsactually fetches from these sources. Consider tracking the actual counts from each source for accuracy.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/utils/actions/report.ts(1 hunks)apps/web/utils/ai/report/analyze-email-behavior.ts(1 hunks)apps/web/utils/ai/report/analyze-label-optimization.ts(1 hunks)apps/web/utils/ai/report/build-user-persona.ts(1 hunks)apps/web/utils/ai/report/generate-actionable-recommendations.ts(1 hunks)apps/web/utils/ai/report/generate-executive-summary.ts(1 hunks)apps/web/utils/ai/report/response-patterns.ts(1 hunks)apps/web/utils/ai/report/summarize-emails.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
apps/web/utils/{ai,llms}/**/*
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
apps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
apps/web/utils/**
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
apps/web/utils/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/ai/report/summarize-emails.tsapps/web/utils/ai/report/analyze-email-behavior.tsapps/web/utils/actions/report.tsapps/web/utils/ai/report/generate-actionable-recommendations.tsapps/web/utils/ai/report/build-user-persona.tsapps/web/utils/ai/report/generate-executive-summary.tsapps/web/utils/ai/report/analyze-label-optimization.tsapps/web/utils/ai/report/response-patterns.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.
Files:
apps/web/utils/actions/report.ts
apps/web/utils/actions/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject in the.action()handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use server
Files:
apps/web/utils/actions/report.ts
🧠 Learnings (8)
apps/web/utils/ai/report/summarize-emails.ts (11)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use descriptive scoped loggers for each LLM feature.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
apps/web/utils/ai/report/analyze-email-behavior.ts (12)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
apps/web/utils/actions/report.ts (19)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start with use server
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement early returns for invalid inputs in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for working with Gmail API
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
apps/web/utils/ai/report/generate-actionable-recommendations.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.
apps/web/utils/ai/report/build-user-persona.ts (12)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
apps/web/utils/ai/report/generate-executive-summary.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
apps/web/utils/ai/report/analyze-label-optimization.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.
apps/web/utils/ai/report/response-patterns.ts (10)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.
🧬 Code Graph Analysis (6)
apps/web/utils/ai/report/analyze-email-behavior.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
apps/web/utils/ai/report/generate-actionable-recommendations.ts (4)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/ai/report/build-user-persona.ts (1)
UserPersona(17-17)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
apps/web/utils/ai/report/build-user-persona.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
apps/web/utils/ai/report/generate-executive-summary.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
apps/web/utils/ai/report/analyze-label-optimization.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
apps/web/utils/ai/report/response-patterns.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
EmailSummary(19-19)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-24)apps/web/utils/llms/index.ts (1)
chatCompletionObject(107-111)
⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (18)
apps/web/utils/ai/report/generate-actionable-recommendations.ts (3)
1-5: LGTM!The imports follow the established patterns and include all necessary dependencies for the LLM function.
7-32: Well-structured Zod schema for actionable recommendations.The schema effectively guides the LLM output with specific categories and detailed field descriptions. The enum constraints for difficulty and impact levels provide clear guidance for structured responses.
39-56: Well-structured system and user prompts.The prompts follow LLM guidelines effectively:
- System prompt clearly defines the AI's role and task specifications
- User prompt contains actual data and context
- Good separation between system and user prompts
- Specific instructions for categorizing recommendations by timeline
apps/web/utils/ai/report/analyze-email-behavior.ts (3)
1-4: LGTM!The imports are properly structured and include all necessary dependencies for the LLM function.
6-23: Well-designed schema for email behavior analysis.The schema effectively structures the expected output with clear categories for timing patterns, content preferences, and engagement triggers. The descriptive field descriptions will guide the LLM to produce structured, useful insights.
30-54: Well-structured prompts for email behavior analysis.The prompts effectively follow LLM guidelines:
- System prompt clearly defines the AI's expertise and focus
- User prompt includes actual email data and context
- Good handling of optional sent email summaries
- Clear instructions for pattern identification
apps/web/utils/ai/report/summarize-emails.ts (5)
1-9: LGTM!Proper implementation of scoped logger and all necessary imports following LLM guidelines.
11-19: Well-defined schema for email summaries.The schema effectively defines the expected structure with descriptive fields that will guide the LLM to produce consistent, structured summaries.
21-52: Excellent implementation following LLM guidelines.This function demonstrates proper LLM patterns:
- Early input validation with logging
- Batch processing to manage API load and rate limits
- Proper delay handling between batches
- Good error handling structure
The batch size of 15 and 1-second delay are reasonable for API rate limiting.
54-96: Exemplary LLM function implementation.This function demonstrates all required LLM guidelines:
- Proper input/output logging with trace level
- Well-separated system and user prompts
- System prompt defines role and rules clearly
- User prompt uses structured formatting with XML-like tags
- Proper schema validation and error handling
- Consistent data formatting with character limits
This serves as a good reference implementation for other LLM functions.
60-80: Well-crafted prompts with clear guidelines.The prompts effectively guide the LLM:
- System prompt provides comprehensive rules including objectivity requirements
- Good filtering guidelines for relevant vs. promotional content
- User prompt uses structured XML-like formatting
- Batch context helps with processing tracking
- Character limit prevents overly long inputs
apps/web/utils/ai/report/analyze-label-optimization.ts (3)
1-5: LGTM!The imports properly include all necessary dependencies including Gmail API types for label analysis.
7-18: Well-structured schema for label optimization.The schema effectively defines optimization suggestions with:
- Clear enum constraints for optimization types
- Impact level categorization
- Descriptive field descriptions that guide LLM output
25-49: Well-structured prompts for label optimization.The prompts effectively follow LLM guidelines:
- System prompt clearly defines expertise and focus area
- User prompt includes actual Gmail label data and email content
- Clear categorization of optimization types
- Structured formatting for better LLM comprehension
apps/web/utils/ai/report/build-user-persona.ts (3)
1-4: LGTM!The imports are properly structured and include all necessary dependencies for the persona building function.
6-17: Well-designed schema for user persona analysis.The schema effectively captures professional identity with supporting evidence and current priorities. The nested structure and descriptive fields will guide the LLM to produce structured, actionable persona insights.
26-64: Excellent prompt structure with good optional data handling.The prompts effectively follow LLM guidelines:
- System prompt clearly defines the AI's analytical role and focus areas
- User prompt includes structured data sections with good conditional formatting
- Excellent handling of optional sent emails, signature, and templates
- Clear instructions for professional identity and priority identification
The conditional prompt construction is particularly well-implemented.
apps/web/utils/actions/report.ts (1)
26-31: LGTM!The server action is correctly implemented using
actionClientwith proper metadata and context access.
| // TODO: should be able to import this functionality from elsewhere | ||
| async function fetchGmailLabels( | ||
| gmail: gmail_v1.Gmail, | ||
| ): Promise<gmail_v1.Schema$Label[]> { | ||
| try { | ||
| const response = await gmail.users.labels.list({ userId: "me" }); | ||
|
|
||
| const userLabels = | ||
| response.data.labels?.filter( | ||
| (label: gmail_v1.Schema$Label) => | ||
| label.type === "user" && | ||
| label.name && | ||
| !label.name.startsWith("CATEGORY_") && | ||
| !label.name.startsWith("CHAT"), | ||
| ) || []; | ||
|
|
||
| const labelsWithCounts = await Promise.all( | ||
| userLabels | ||
| .filter( | ||
| ( | ||
| label, | ||
| ): label is gmail_v1.Schema$Label & { id: string; name: string } => | ||
| Boolean(label.id && label.name), | ||
| ) | ||
| .map(async (label) => { | ||
| try { | ||
| const labelDetail = await gmail.users.labels.get({ | ||
| userId: "me", | ||
| id: label.id, | ||
| }); | ||
| return { | ||
| ...label, | ||
| messagesTotal: labelDetail.data.messagesTotal || 0, | ||
| messagesUnread: labelDetail.data.messagesUnread || 0, | ||
| threadsTotal: labelDetail.data.threadsTotal || 0, | ||
| threadsUnread: labelDetail.data.threadsUnread || 0, | ||
| }; | ||
| } catch (error) { | ||
| logger.warn(`Failed to get details for label ${label.name}:`, { | ||
| error: error instanceof Error ? error.message : String(error), | ||
| }); | ||
| return { | ||
| ...label, | ||
| messagesTotal: 0, | ||
| messagesUnread: 0, | ||
| threadsTotal: 0, | ||
| threadsUnread: 0, | ||
| }; | ||
| } | ||
| }), | ||
| ); | ||
|
|
||
| const sortedLabels = labelsWithCounts.sort( | ||
| (a, b) => (b.messagesTotal || 0) - (a.messagesTotal || 0), | ||
| ); | ||
|
|
||
| return sortedLabels; | ||
| } catch (error) { | ||
| logger.warn("Failed to fetch Gmail labels:", { | ||
| error: error instanceof Error ? error.message : String(error), | ||
| }); | ||
| return []; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Move Gmail-specific functionality to utils/gmail directory
The TODO comment correctly identifies that this Gmail-specific functionality should be imported from elsewhere. According to the coding guidelines, Gmail-related code should be organized in apps/web/utils/gmail/.
Consider creating a shared module like apps/web/utils/gmail/labels.ts to export this functionality:
// apps/web/utils/gmail/labels.ts
export async function fetchGmailLabels(gmail: gmail_v1.Gmail): Promise<gmail_v1.Schema$Label[]> {
// Move the implementation here
}Then import it in this file:
+import { fetchGmailLabels } from "@/utils/gmail/labels";
-// TODO: should be able to import this functionality from elsewhere
-async function fetchGmailLabels(
- gmail: gmail_v1.Gmail,
-): Promise<gmail_v1.Schema$Label[]> {
- // ... implementation
-}🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts between lines 153 and 216, the
Gmail-specific function fetchGmailLabels should be moved to a dedicated module
under apps/web/utils/gmail/, for example apps/web/utils/gmail/labels.ts. Create
this new file, move the entire fetchGmailLabels function implementation there,
export it, and then import and use it in the original file. This will organize
Gmail-related code according to the project's coding guidelines.
| // TODO: should be able to import this functionality from elsewhere | ||
| async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> { | ||
| try { | ||
| const sendAsList = await gmail.users.settings.sendAs.list({ | ||
| userId: "me", | ||
| }); | ||
|
|
||
| if (!sendAsList.data.sendAs || sendAsList.data.sendAs.length === 0) { | ||
| logger.warn("No sendAs settings found"); | ||
| return ""; | ||
| } | ||
|
|
||
| const primarySendAs = sendAsList.data.sendAs[0]; | ||
| if (!primarySendAs.sendAsEmail) { | ||
| logger.warn("No primary sendAs email found"); | ||
| return ""; | ||
| } | ||
|
|
||
| const signatureResponse = await gmail.users.settings.sendAs.get({ | ||
| userId: "me", | ||
| sendAsEmail: primarySendAs.sendAsEmail, | ||
| }); | ||
|
|
||
| const signature = signatureResponse.data.signature; | ||
| logger.info("Gmail signature fetched successfully", { | ||
| hasSignature: !!signature, | ||
| sendAsEmail: primarySendAs.sendAsEmail, | ||
| }); | ||
|
|
||
| return signature || ""; | ||
| } catch (error) { | ||
| logger.warn("Failed to fetch Gmail signature:", { | ||
| error: error instanceof Error ? error.message : String(error), | ||
| }); | ||
| return ""; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Move Gmail signature functionality to utils/gmail directory
Similar to fetchGmailLabels, this Gmail-specific functionality should be moved to the appropriate utils subfolder.
Create a module like apps/web/utils/gmail/signature.ts:
// apps/web/utils/gmail/signature.ts
export async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> {
// Move the implementation here
}Then import it:
+import { fetchGmailSignature } from "@/utils/gmail/signature";
-// TODO: should be able to import this functionality from elsewhere
-async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> {
- // ... implementation
-}🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts from lines 218 to 254, the
fetchGmailSignature function contains Gmail-specific logic that should be moved
to a dedicated module for better organization. Create a new file at
apps/web/utils/gmail/signature.ts and move the entire fetchGmailSignature
function implementation there, exporting it. Then, update the original file to
import fetchGmailSignature from the new location instead of defining it inline.
| export async function aiAnalyzeEmailBehavior( | ||
| emailSummaries: EmailSummary[], | ||
| emailAccount: EmailAccountWithAI, | ||
| sentEmailSummaries?: EmailSummary[], | ||
| ) { | ||
| const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities. | ||
|
|
||
| Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`; | ||
|
|
||
| const prompt = `### Email Analysis Data | ||
|
|
||
| **Received Emails:** | ||
| ${emailSummaries.map((email, i) => `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")} | ||
|
|
||
| ${ | ||
| sentEmailSummaries && sentEmailSummaries.length > 0 | ||
| ? ` | ||
| **Sent Emails:** | ||
| ${sentEmailSummaries.map((email, i) => `${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")} | ||
| ` | ||
| : "" | ||
| } | ||
|
|
||
| --- | ||
|
|
||
| Analyze the email patterns and identify: | ||
| 1. Timing patterns (when emails are most active, response preferences) | ||
| 2. Content preferences (what types of emails they engage with vs avoid) | ||
| 3. Engagement triggers (what prompts them to take action) | ||
| 4. Specific automation opportunities with estimated time savings`; | ||
|
|
||
| const result = await chatCompletionObject({ | ||
| userAi: emailAccount.user, | ||
| system, | ||
| prompt, | ||
| schema: emailBehaviorSchema, | ||
| userEmail: emailAccount.email, | ||
| usageLabel: "email-report-email-behavior", | ||
| }); | ||
|
|
||
| return result.object; | ||
| } |
There was a problem hiding this comment.
Missing critical LLM function requirements.
The function is missing several elements required by the LLM coding guidelines:
- Scoped logger: Should use
createScopedLoggerfor tracing - Input validation: Should validate inputs early
- Logging: Should log inputs and outputs
- Error handling: Missing fallbacks for AI failures
Add the missing elements:
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("email-behavior-analysis");
+
export async function aiAnalyzeEmailBehavior(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
) {
+ // Early input validation
+ if (!emailSummaries.length) {
+ logger.warn("No email summaries provided for behavior analysis");
+ return {
+ timingPatterns: { peakHours: [], responsePreference: "", frequency: "" },
+ contentPreferences: { preferred: [], avoided: [] },
+ engagementTriggers: []
+ };
+ }
+
const system = `You are an expert AI system...`;
const prompt = `### Email Analysis Data...`;
+
+ logger.trace("Input", {
+ receivedEmailCount: emailSummaries.length,
+ sentEmailCount: sentEmailSummaries?.length || 0,
+ system,
+ prompt
+ });
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: emailBehaviorSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-email-behavior",
});
+ logger.trace("Output", { result: result.object });
+
return result.object;
}📝 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.
| export async function aiAnalyzeEmailBehavior( | |
| emailSummaries: EmailSummary[], | |
| emailAccount: EmailAccountWithAI, | |
| sentEmailSummaries?: EmailSummary[], | |
| ) { | |
| const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities. | |
| Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`; | |
| const prompt = `### Email Analysis Data | |
| **Received Emails:** | |
| ${emailSummaries.map((email, i) => `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")} | |
| ${ | |
| sentEmailSummaries && sentEmailSummaries.length > 0 | |
| ? ` | |
| **Sent Emails:** | |
| ${sentEmailSummaries.map((email, i) => `${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")} | |
| ` | |
| : "" | |
| } | |
| --- | |
| Analyze the email patterns and identify: | |
| 1. Timing patterns (when emails are most active, response preferences) | |
| 2. Content preferences (what types of emails they engage with vs avoid) | |
| 3. Engagement triggers (what prompts them to take action) | |
| 4. Specific automation opportunities with estimated time savings`; | |
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: emailBehaviorSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-email-behavior", | |
| }); | |
| return result.object; | |
| } | |
| import { createScopedLogger } from "@/utils/logger"; | |
| const logger = createScopedLogger("email-behavior-analysis"); | |
| export async function aiAnalyzeEmailBehavior( | |
| emailSummaries: EmailSummary[], | |
| emailAccount: EmailAccountWithAI, | |
| sentEmailSummaries?: EmailSummary[], | |
| ) { | |
| // Early input validation | |
| if (!emailSummaries.length) { | |
| logger.warn("No email summaries provided for behavior analysis"); | |
| return { | |
| timingPatterns: { peakHours: [], responsePreference: "", frequency: "" }, | |
| contentPreferences: { preferred: [], avoided: [] }, | |
| engagementTriggers: [] | |
| }; | |
| } | |
| const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities. | |
| Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`; | |
| const prompt = `### Email Analysis Data | |
| **Received Emails:** | |
| ${emailSummaries | |
| .map( | |
| (email, i) => | |
| `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}` | |
| ) | |
| .join("\n")} | |
| ${ | |
| sentEmailSummaries && sentEmailSummaries.length > 0 | |
| ? ` | |
| **Sent Emails:** | |
| ${sentEmailSummaries | |
| .map( | |
| (email, i) => | |
| `${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}` | |
| ) | |
| .join("\n")} | |
| ` | |
| : "" | |
| } | |
| --- | |
| Analyze the email patterns and identify: | |
| 1. Timing patterns (when emails are most active, response preferences) | |
| 2. Content preferences (what types of emails they engage with vs avoid) | |
| 3. Engagement triggers (what prompts them to take action) | |
| 4. Specific automation opportunities with estimated time savings`; | |
| logger.trace("Input", { | |
| receivedEmailCount: emailSummaries.length, | |
| sentEmailCount: sentEmailSummaries?.length || 0, | |
| system, | |
| prompt, | |
| }); | |
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: emailBehaviorSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-email-behavior", | |
| }); | |
| logger.trace("Output", { result: result.object }); | |
| return result.object; | |
| } |
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/analyze-email-behavior.ts around lines 25 to 66, the
function aiAnalyzeEmailBehavior lacks a scoped logger, input validation, logging
of inputs and outputs, and error handling for AI failures. Fix this by importing
and using createScopedLogger to create a scoped logger instance, validate the
inputs at the start of the function to ensure they meet expected types and
conditions, add logging statements to record the received inputs and the AI
response, and wrap the AI call in a try-catch block to handle errors gracefully
with fallback behavior and error logging.
| export async function aiAnalyzeLabelOptimization( | ||
| emailSummaries: EmailSummary[], | ||
| emailAccount: EmailAccountWithAI, | ||
| gmailLabels: gmail_v1.Schema$Label[], | ||
| ): Promise<z.infer<typeof labelAnalysisSchema>> { | ||
| const system = `You are a Gmail organization expert. Analyze the user's current labels and email patterns to suggest specific optimizations that will improve their email organization and workflow efficiency. | ||
|
|
||
| Focus on practical suggestions that will reduce email management time and improve organization.`; | ||
|
|
||
| const prompt = `### Current Gmail Labels | ||
| ${gmailLabels.map((label) => `- ${label.name}: ${label.messagesTotal || 0} emails, ${label.messagesUnread || 0} unread`).join("\n")} | ||
|
|
||
| ### Email Content Analysis | ||
| ${emailSummaries | ||
| .slice(0, 30) | ||
| .map( | ||
| (email, i) => | ||
| `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`, | ||
| ) | ||
| .join("\n")} | ||
|
|
||
| --- | ||
|
|
||
| Based on the current labels and email content, suggest specific optimizations: | ||
| 1. Labels to create based on email patterns | ||
| 2. Labels to consolidate that have overlapping purposes | ||
| 3. Labels to rename for better clarity | ||
| 4. Labels to delete that are unused or redundant | ||
|
|
||
| Each suggestion should include the reason and expected impact.`; | ||
|
|
||
| const result = await chatCompletionObject({ | ||
| userAi: emailAccount.user, | ||
| system, | ||
| prompt, | ||
| schema: labelAnalysisSchema, | ||
| userEmail: emailAccount.email, | ||
| usageLabel: "email-report-label-analysis", | ||
| }); | ||
|
|
||
| return result.object; | ||
| } |
There was a problem hiding this comment.
Missing critical LLM function requirements.
The function is missing several elements required by the LLM coding guidelines:
- Scoped logger: Should use
createScopedLoggerfor tracing - Input validation: Should validate inputs early
- Logging: Should log inputs and outputs
- Error handling: Missing fallbacks for AI failures
Add the missing elements:
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("label-optimization");
+
export async function aiAnalyzeLabelOptimization(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
gmailLabels: gmail_v1.Schema$Label[],
): Promise<z.infer<typeof labelAnalysisSchema>> {
+ // Early input validation
+ if (!emailSummaries.length && !gmailLabels.length) {
+ logger.warn("No data provided for label optimization analysis");
+ return { optimizationSuggestions: [] };
+ }
+
const system = `You are a Gmail organization expert...`;
const prompt = `### Current Gmail Labels...`;
+
+ logger.trace("Input", {
+ emailCount: emailSummaries.length,
+ labelCount: gmailLabels.length,
+ system,
+ prompt
+ });
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: labelAnalysisSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-label-analysis",
});
+ logger.trace("Output", { result: result.object });
+
return result.object;
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/analyze-label-optimization.ts between lines 20 and
61, the function lacks a scoped logger, input validation, logging of inputs and
outputs, and error handling for AI failures. Fix this by importing and using
createScopedLogger to create a logger scoped to this function, validate the
inputs early using appropriate schema validation or checks, log the received
inputs before processing and the AI response after receiving it, and wrap the AI
call in a try-catch block to handle errors gracefully with fallback behavior or
error logging.
| export async function aiBuildUserPersona( | ||
| emailSummaries: EmailSummary[], | ||
| emailAccount: EmailAccountWithAI, | ||
| sentEmailSummaries?: EmailSummary[], | ||
| gmailSignature?: string, | ||
| gmailTemplates?: string[], | ||
| ): Promise<z.infer<typeof userPersonaSchema>> { | ||
| const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity. | ||
|
|
||
| Analyze the email summaries, signatures, and templates to identify: | ||
| 1. Professional identity with supporting evidence | ||
| 2. Current professional priorities based on email content | ||
|
|
||
| Focus on understanding the user's role and what they're currently focused on professionally.`; | ||
|
|
||
| const prompt = `### Input Data | ||
|
|
||
| **Received Email Summaries:** | ||
| ${emailSummaries.map((summary, index) => `Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")} | ||
|
|
||
| ${ | ||
| sentEmailSummaries && sentEmailSummaries.length > 0 | ||
| ? ` | ||
| **Sent Email Summaries:** | ||
| ${sentEmailSummaries.map((summary, index) => `Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")} | ||
| ` | ||
| : "" | ||
| } | ||
|
|
||
| **User's Signature:** | ||
| ${gmailSignature || "[No signature data available – analyze based on email content only]"} | ||
|
|
||
| ${ | ||
| gmailTemplates && gmailTemplates.length > 0 | ||
| ? ` | ||
| **User's Gmail Templates:** | ||
| ${gmailTemplates.map((template, index) => `Template ${index + 1}: ${template}`).join("\n")} | ||
| ` | ||
| : "" | ||
| } | ||
|
|
||
| --- | ||
|
|
||
| Analyze the data and identify: | ||
| 1. **Professional Identity**: What is their role and what evidence supports this? | ||
| 2. **Current Priorities**: What are they focused on professionally based on email content?`; | ||
|
|
||
| const result = await chatCompletionObject({ | ||
| userAi: emailAccount.user, | ||
| system, | ||
| prompt, | ||
| schema: userPersonaSchema, | ||
| userEmail: emailAccount.email, | ||
| usageLabel: "email-report-user-persona", | ||
| }); | ||
|
|
||
| return result.object; | ||
| } |
There was a problem hiding this comment.
Missing critical LLM function requirements but good optional parameter handling.
The function is missing several elements required by the LLM coding guidelines:
- Scoped logger: Should use
createScopedLoggerfor tracing - Input validation: Should validate inputs early
- Logging: Should log inputs and outputs
- Error handling: Missing fallbacks for AI failures
However, the optional parameter handling for sent emails, signature, and templates is well-implemented.
Add the missing elements:
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("user-persona");
+
export async function aiBuildUserPersona(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
gmailSignature?: string,
gmailTemplates?: string[],
): Promise<z.infer<typeof userPersonaSchema>> {
+ // Early input validation
+ if (!emailSummaries.length) {
+ logger.warn("No email summaries provided for persona building");
+ return {
+ professionalIdentity: { persona: "", supportingEvidence: [] },
+ currentPriorities: []
+ };
+ }
+
const system = `You are a highly skilled AI analyst...`;
const prompt = `### Input Data...`;
+
+ logger.trace("Input", {
+ receivedEmailCount: emailSummaries.length,
+ sentEmailCount: sentEmailSummaries?.length || 0,
+ hasSignature: !!gmailSignature,
+ templateCount: gmailTemplates?.length || 0,
+ system,
+ prompt
+ });
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: userPersonaSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-user-persona",
});
+ logger.trace("Output", { result: result.object });
+
return result.object;
}📝 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.
| export async function aiBuildUserPersona( | |
| emailSummaries: EmailSummary[], | |
| emailAccount: EmailAccountWithAI, | |
| sentEmailSummaries?: EmailSummary[], | |
| gmailSignature?: string, | |
| gmailTemplates?: string[], | |
| ): Promise<z.infer<typeof userPersonaSchema>> { | |
| const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity. | |
| Analyze the email summaries, signatures, and templates to identify: | |
| 1. Professional identity with supporting evidence | |
| 2. Current professional priorities based on email content | |
| Focus on understanding the user's role and what they're currently focused on professionally.`; | |
| const prompt = `### Input Data | |
| **Received Email Summaries:** | |
| ${emailSummaries.map((summary, index) => `Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")} | |
| ${ | |
| sentEmailSummaries && sentEmailSummaries.length > 0 | |
| ? ` | |
| **Sent Email Summaries:** | |
| ${sentEmailSummaries.map((summary, index) => `Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")} | |
| ` | |
| : "" | |
| } | |
| **User's Signature:** | |
| ${gmailSignature || "[No signature data available – analyze based on email content only]"} | |
| ${ | |
| gmailTemplates && gmailTemplates.length > 0 | |
| ? ` | |
| **User's Gmail Templates:** | |
| ${gmailTemplates.map((template, index) => `Template ${index + 1}: ${template}`).join("\n")} | |
| ` | |
| : "" | |
| } | |
| --- | |
| Analyze the data and identify: | |
| 1. **Professional Identity**: What is their role and what evidence supports this? | |
| 2. **Current Priorities**: What are they focused on professionally based on email content?`; | |
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: userPersonaSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-user-persona", | |
| }); | |
| return result.object; | |
| } | |
| import { createScopedLogger } from "@/utils/logger"; | |
| const logger = createScopedLogger("user-persona"); | |
| export async function aiBuildUserPersona( | |
| emailSummaries: EmailSummary[], | |
| emailAccount: EmailAccountWithAI, | |
| sentEmailSummaries?: EmailSummary[], | |
| gmailSignature?: string, | |
| gmailTemplates?: string[], | |
| ): Promise<z.infer<typeof userPersonaSchema>> { | |
| // Early input validation | |
| if (!emailSummaries.length) { | |
| logger.warn("No email summaries provided for persona building"); | |
| return { | |
| professionalIdentity: { persona: "", supportingEvidence: [] }, | |
| currentPriorities: [] | |
| }; | |
| } | |
| const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity. | |
| Analyze the email summaries, signatures, and templates to identify: | |
| 1. Professional identity with supporting evidence | |
| 2. Current professional priorities based on email content | |
| Focus on understanding the user's role and what they're currently focused on professionally.`; | |
| const prompt = `### Input Data | |
| **Received Email Summaries:** | |
| ${emailSummaries | |
| .map( | |
| (summary, index) => | |
| `Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})` | |
| ) | |
| .join("\n")} | |
| ${ | |
| sentEmailSummaries && sentEmailSummaries.length > 0 | |
| ? ` | |
| **Sent Email Summaries:** | |
| ${sentEmailSummaries | |
| .map( | |
| (summary, index) => | |
| `Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})` | |
| ) | |
| .join("\n")} | |
| ` | |
| : "" | |
| } | |
| **User's Signature:** | |
| ${gmailSignature || "[No signature data available – analyze based on email content only]"} | |
| ${ | |
| gmailTemplates && gmailTemplates.length > 0 | |
| ? ` | |
| **User's Gmail Templates:** | |
| ${gmailTemplates | |
| .map((template, index) => `Template ${index + 1}: ${template}`) | |
| .join("\n")} | |
| ` | |
| : "" | |
| } | |
| --- | |
| Analyze the data and identify: | |
| 1. **Professional Identity**: What is their role and what evidence supports this? | |
| 2. **Current Priorities**: What are they focused on professionally based on email content?`; | |
| logger.trace("Input", { | |
| receivedEmailCount: emailSummaries.length, | |
| sentEmailCount: sentEmailSummaries?.length || 0, | |
| hasSignature: !!gmailSignature, | |
| templateCount: gmailTemplates?.length || 0, | |
| system, | |
| prompt, | |
| }); | |
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: userPersonaSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-user-persona", | |
| }); | |
| logger.trace("Output", { result: result.object }); | |
| return result.object; | |
| } |
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/build-user-persona.ts from lines 19 to 76, the
function aiBuildUserPersona lacks critical LLM requirements: add a scoped logger
using createScopedLogger at the start for tracing; validate inputs early to
ensure they meet expected formats or types; add logging statements to record
input parameters and the AI response output; implement error handling with
try-catch around the AI call to provide fallback behavior or error messages if
the AI request fails. Keep the existing optional parameter handling as is.
| export async function aiGenerateExecutiveSummary( | ||
| emailSummaries: EmailSummary[], | ||
| sentEmailSummaries: EmailSummary[], | ||
| gmailLabels: gmail_v1.Schema$Label[], | ||
| emailAccount: EmailAccountWithAI, | ||
| ): Promise<z.infer<typeof executiveSummarySchema>> { |
There was a problem hiding this comment.
Add input validation and early returns
Implement early validation for inputs as required by the coding guidelines.
export async function aiGenerateExecutiveSummary(
emailSummaries: EmailSummary[],
sentEmailSummaries: EmailSummary[],
gmailLabels: gmail_v1.Schema$Label[],
emailAccount: EmailAccountWithAI,
): Promise<z.infer<typeof executiveSummarySchema>> {
+ // Validate inputs early
+ if (!emailSummaries || emailSummaries.length === 0) {
+ logger.warn("No email summaries provided for executive summary");
+ return {
+ userProfile: {
+ persona: "Unknown",
+ confidence: 0,
+ },
+ topInsights: [],
+ quickActions: [],
+ };
+ }
+
+ logger.info("Starting executive summary generation", {
+ receivedCount: emailSummaries.length,
+ sentCount: sentEmailSummaries.length,
+ labelsCount: gmailLabels.length,
+ emailAccount: emailAccount.email,
+ });🤖 Prompt for AI Agents
In apps/web/utils/ai/report/generate-executive-summary.ts around lines 48 to 53,
the function aiGenerateExecutiveSummary lacks input validation. Add checks at
the start of the function to validate that emailSummaries, sentEmailSummaries,
gmailLabels, and emailAccount meet expected criteria, and return early or throw
errors if inputs are invalid to prevent further processing with bad data.
| const result = await chatCompletionObject({ | ||
| userAi: emailAccount.user, | ||
| system, | ||
| prompt, | ||
| schema: executiveSummarySchema, | ||
| userEmail: emailAccount.email, | ||
| usageLabel: "email-report-executive-summary", | ||
| }); | ||
|
|
||
| return result.object; | ||
| } |
There was a problem hiding this comment.
Add error handling, logging, and fallbacks
Implement proper error handling with fallbacks and log the results.
- const result = await chatCompletionObject({
- userAi: emailAccount.user,
- system,
- prompt,
- schema: executiveSummarySchema,
- userEmail: emailAccount.email,
- usageLabel: "email-report-executive-summary",
- });
-
- return result.object;
+ try {
+ const result = await chatCompletionObject({
+ userAi: emailAccount.user,
+ system,
+ prompt,
+ schema: executiveSummarySchema,
+ userEmail: emailAccount.email,
+ usageLabel: "email-report-executive-summary",
+ });
+
+ logger.info("Executive summary generated successfully", {
+ persona: result.object.userProfile.persona,
+ confidence: result.object.userProfile.confidence,
+ insightsCount: result.object.topInsights.length,
+ actionsCount: result.object.quickActions.length,
+ });
+
+ return result.object;
+ } catch (error) {
+ logger.error("Failed to generate executive summary", {
+ error: error instanceof Error ? error.message : String(error),
+ });
+
+ // Return fallback response
+ return {
+ userProfile: {
+ persona: "Professional",
+ confidence: 0,
+ },
+ topInsights: [],
+ quickActions: [],
+ };
+ }🤖 Prompt for AI Agents
In apps/web/utils/ai/report/generate-executive-summary.ts around lines 136 to
146, the code lacks error handling for the async call to chatCompletionObject.
Wrap the call in a try-catch block, log any errors encountered, and provide a
fallback return value to ensure the function handles failures gracefully without
crashing.
| const result = await chatCompletionObject({ | ||
| userAi: emailAccount.user, | ||
| system, | ||
| prompt, | ||
| schema: responsePatternsSchema, | ||
| userEmail: emailAccount.email, | ||
| usageLabel: "email-report-response-patterns", | ||
| }); | ||
|
|
||
| return result.object; | ||
| } |
There was a problem hiding this comment.
Add error handling, logging, and fallbacks for AI failures
The function must implement proper error handling with fallbacks and log the results as required by the coding guidelines.
- const result = await chatCompletionObject({
- userAi: emailAccount.user,
- system,
- prompt,
- schema: responsePatternsSchema,
- userEmail: emailAccount.email,
- usageLabel: "email-report-response-patterns",
- });
-
- return result.object;
+ try {
+ const result = await chatCompletionObject({
+ userAi: emailAccount.user,
+ system,
+ prompt,
+ schema: responsePatternsSchema,
+ userEmail: emailAccount.email,
+ usageLabel: "email-report-response-patterns",
+ });
+
+ logger.info("Response pattern analysis completed successfully", {
+ commonResponsesCount: result.object.commonResponses.length,
+ templatesCount: result.object.suggestedTemplates.length,
+ categoriesCount: result.object.categoryOrganization.length,
+ });
+
+ return result.object;
+ } catch (error) {
+ logger.error("Failed to analyze response patterns", {
+ error: error instanceof Error ? error.message : String(error),
+ });
+
+ // Return fallback response
+ return {
+ commonResponses: [],
+ suggestedTemplates: [],
+ categoryOrganization: [],
+ };
+ }📝 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.
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: responsePatternsSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-response-patterns", | |
| }); | |
| return result.object; | |
| } | |
| try { | |
| const result = await chatCompletionObject({ | |
| userAi: emailAccount.user, | |
| system, | |
| prompt, | |
| schema: responsePatternsSchema, | |
| userEmail: emailAccount.email, | |
| usageLabel: "email-report-response-patterns", | |
| }); | |
| logger.info("Response pattern analysis completed successfully", { | |
| commonResponsesCount: result.object.commonResponses.length, | |
| templatesCount: result.object.suggestedTemplates.length, | |
| categoriesCount: result.object.categoryOrganization.length, | |
| }); | |
| return result.object; | |
| } catch (error) { | |
| logger.error("Failed to analyze response patterns", { | |
| error: error instanceof Error ? error.message : String(error), | |
| }); | |
| // Return fallback response | |
| return { | |
| commonResponses: [], | |
| suggestedTemplates: [], | |
| categoryOrganization: [], | |
| }; | |
| } | |
| } |
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/response-patterns.ts around lines 87 to 97, the
function calling chatCompletionObject lacks error handling and logging. Wrap the
call in a try-catch block, log any errors encountered, and provide a fallback
return value to ensure the function handles AI failures gracefully according to
coding guidelines.
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (3)
.cursor/rules/llm.mdc (3)
1-5: Frontmatter: add required globs and make the rule target explicitPer project rules, frontmatter must include globs that scope where this rule applies. It’s currently empty.
Apply this diff to define explicit targets (aligned with our retrieved learnings and current code organization):
--- description: Guidelines for implementing LLM (Language Model) functionality in the application -globs: +globs: + - apps/web/utils/ai/**/*.ts + - apps/web/utils/llms/**/*.ts + - apps/web/__tests__/**/*.ts alwaysApply: false ---Nit: consider renaming this file to a more descriptive kebab-case name, e.g. llm-implementation-guidelines.mdc.
26-31: Fix imports and initialize a scoped logger for the sample codeThe example imports UserEmailWithAI (unused) while the function signature uses EmailAccountWithAI. Also, logger is referenced but never defined.
Apply this diff to correct types and define the logger:
import { z } from "zod"; import { createScopedLogger } from "@/utils/logger"; import { chatCompletionObject } from "@/utils/llms"; -import type { UserEmailWithAI } from "@/utils/llms/types"; +import type { EmailAccountWithAI } from "@/utils/llms/types"; + +const logger = createScopedLogger("featureFunction");
57-71: Inconsistent user reference: userEmail still uses “user” which no longer existsAfter switching to emailAccount, userEmail still references user.email. This will not compile in consumers if they follow the snippet literally.
Apply this diff:
const result = await chatCompletionObject({ - userAi: emailAccount.user, + userAi: emailAccount.user, system, prompt, schema: z.object({ field1: z.string(), field2: z.number(), nested: z.object({ subfield: z.string(), }), array_field: z.array(z.string()), }), - userEmail: user.email, + userEmail: emailAccount.user.email, usageLabel: "Feature Name", });Optional: demonstrate robust error handling/retry as per Best Practices:
// try/catch with retry for transient LLM errors // const result = await withRetry(() => chatCompletionObject({ ... }), { tries: 3, delayMs: 250 });
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.cursor/rules/llm.mdc(3 hunks)apps/web/utils/ai/knowledge/writing-style.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/utils/ai/knowledge/writing-style.ts
🧰 Additional context used
📓 Path-based instructions (3)
.cursor/rules/*.mdc
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule
Files:
.cursor/rules/llm.mdc
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
.cursor/rules/llm.mdc
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
.cursor/rules/llm.mdc
🧠 Learnings (6)
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related functions.
Applied to files:
.cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.
Applied to files:
.cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related functions.
Applied to files:
.cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Log inputs and outputs with appropriate log levels in LLM-related functions.
Applied to files:
.cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.
Applied to files:
.cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.
Applied to files:
.cursor/rules/llm.mdc
⏰ 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: Static Code Analysis Js
- GitHub Check: Jit Security
Summary by CodeRabbit
New Features
Chores