Unify pricing to Free & Pro (0/mo), Autumn billing, Convex cost controls, Kimi K2 models#93
Conversation
…portal; consolidate Autumn wrapper; reduce Convex writes on home; default sequential thinking to Kimi K2; update env example
…on; refine pricing test plans
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughIntroduces Autumn-based checkout and billing-portal API routes with Stripe fallbacks, consolidates Autumn wrapper, updates pricing UIs to a Free/Pro model, adds a public Stripe Pro price ID env var, modifies home page to manual “Save preview,” and refactors sequential-thinking to a provider-agnostic engine with a minor interface extension. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User (Client)
participant P as Next.js API\nPOST /api/autumn/checkout
participant A as Autumn Service
participant S as Stripe Service
U->>P: POST { priceId? }
alt AUTUMN_SECRET_KEY set
P->>A: autumn.checkout(mockCtx, { priceId })
alt Autumn returns URL
A-->>P: { url | sessionUrl | redirectUrl }
P-->>U: { url }
else Autumn fails or no URL
P->>S: POST /api/stripe/create-checkout-session { priceId|env }
alt Stripe OK
S-->>P: { sessionId }
P-->>U: { provider: "stripe", sessionId }
else Stripe error
S-->>P: 4xx/5xx
P-->>U: 500 error
end
end
else No Autumn configured
P->>S: POST /api/stripe/create-checkout-session
S-->>P: { sessionId } or error
P-->>U: sessionId or 500
end
sequenceDiagram
autonumber
actor U as User (Client)
participant P as Next.js API\nPOST /api/autumn/billing-portal
participant A as Autumn Service
participant S as Stripe Service
U->>P: POST { returnUrl? } with auth
alt Auth missing
P-->>U: 401 { error: "Unauthorized" }
else Auth OK
alt AUTUMN_SECRET_KEY set
P->>A: autumn.billingPortal(mockCtx, { returnUrl })
alt Autumn returns URL
A-->>P: { url | portalUrl | redirectUrl }
P-->>U: { url }
else Autumn fails
P->>S: POST /api/stripe/customer-portal { returnUrl }
S-->>P: { url } or error
P-->>U: { url } or 500
end
else No Autumn configured
P->>S: POST /api/stripe/customer-portal
S-->>P: { url } or error
P-->>U: { url } or 500
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Flag potential breaking changes that are not documented:
1. Identify changes to public APIs/exports, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints (including removed/renamed items and changes to types, required params, return values, defaults, or behavior).
2. Ignore purely internal/private changes (e.g., code not exported from package entry points or marked internal).
3. Verify documentation exists: a "Breaking Change" section in the PR description and updates to CHANGELOG.md.Please share your feedback with us on this Discord post. ✨ 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. Comment |
There was a problem hiding this comment.
This PR is being reviewed by Cursor Bugbot
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, please upgrade to Bugbot Pro by visiting the Cursor dashboard. Your first 14 days will be free!
…s; Autumn checkout on pricing page; fallback components normalized
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
components/MasterDashboard.tsx (1)
133-139: Make Monitoring gating consistent: Pro and Enterprise should see it.Nav still restricts Monitoring to Enterprise, but content allows Pro. Unify to “non-free” across nav, content, and the restricted message.
{ id: 'monitoring', label: 'Monitoring', icon: Monitor, description: 'Real-time system monitoring', - available: userSubscription === 'enterprise' + available: userSubscription !== 'free' }- title={!isAvailable ? `${item.label} requires Pro subscription` : item.description} + title={!isAvailable ? `${item.label} requires Pro subscription` : item.description}(Title can remain “Pro,” since gating becomes non-free.)
-{currentView === 'monitoring' && userSubscription !== 'free' && ( +{currentView === 'monitoring' && userSubscription !== 'free' && ( <div className="text-center py-12">(Already correct; kept for clarity.)
-{((currentView === 'autonomous' && userSubscription === 'free') || - (currentView === 'analytics' && userSubscription === 'free') || - (currentView === 'monitoring' && userSubscription !== 'enterprise')) && ( +{((currentView === 'autonomous' && userSubscription === 'free') || + (currentView === 'analytics' && userSubscription === 'free') || + (currentView === 'monitoring' && userSubscription === 'free')) && ( <div className="text-center py-12">Also applies to: 211-218, 412-422, 424-440
app/page.tsx (2)
1733-1748: Set currentChatId when a chat is selected to enable persistence and preview saving.Without this, user messages aren’t saved and “Save preview” stays hidden.
- <ConvexChat - onChatSelect={(chatId) => { - // Handle chat selection - could load chat context - console.log('Selected chat:', chatId); - // TODO: Load chat messages into current conversation context - }} + <ConvexChat + onChatSelect={(chatId) => { + setCurrentChatId(chatId); + console.log('Selected chat:', chatId); + // TODO: Load chat messages into current conversation context + }}
139-142: Fix race between sandbox creation and code application (stale sandboxData).createSandbox completes before React state is observable within the same tick; processAIMessage may attempt to apply code with a null/old sandbox. Track the latest sandbox in a ref and consume it where needed.
Add a ref and keep it synced:
@@ - const iframeRef = useRef<HTMLIFrameElement>(null); + const iframeRef = useRef<HTMLIFrameElement>(null); const chatMessagesRef = useRef<HTMLDivElement>(null); const codeDisplayRef = useRef<HTMLDivElement>(null); + const sandboxRef = useRef<SandboxData | null>(null); + useEffect(() => { + sandboxRef.current = sandboxData; + }, [sandboxData]);Update on successful sandbox creation:
@@ - setSandboxData(data); + setSandboxData(data); + sandboxRef.current = data;Use the ref when sending apply requests:
@@ - sandboxId: sandboxData?.sandboxId // Pass the sandbox ID to ensure proper connection + sandboxId: (sandboxRef.current ?? sandboxData)?.sandboxIdUse the ref for iframe refresh URLs:
@@ - if (iframeRef.current && sandboxData?.url) { + const sd = sandboxRef.current ?? sandboxData; + if (iframeRef.current && sd?.url) { console.log('[home] Refreshing iframe after code application...'); - const urlWithTimestamp = `${sandboxData.url}?t=${Date.now()}&applied=true`; + const urlWithTimestamp = `${sd.url}?t=${Date.now()}&applied=true`; iframeRef.current.src = urlWithTimestamp;@@ - if (iframeRef.current && sandboxData?.url) { + const sd = sandboxRef.current ?? sandboxData; + if (iframeRef.current && sd?.url) { console.log('[applyGeneratedCode] Starting iframe refresh sequence...'); - console.log('[applyGeneratedCode] Current iframe src:', iframeRef.current.src); - console.log('[applyGeneratedCode] Sandbox URL:', sandboxData.url); + console.log('[applyGeneratedCode] Current iframe src:', iframeRef.current.src); + console.log('[applyGeneratedCode] Sandbox URL:', sd.url); @@ - const urlWithTimestamp = `${sandboxData.url}?t=${Date.now()}&force=true`; + const urlWithTimestamp = `${sd.url}?t=${Date.now()}&force=true`; @@ - newIframe.src = `${sandboxData.url}?t=${Date.now()}&recreated=true`; + newIframe.src = `${sd.url}?t=${Date.now()}&recreated=true`;Optionally, also prefer the ref in other sandboxData?.url checks in this file to eliminate remaining edge cases.
Also applies to: 439-445, 548-553, 894-901, 920-943
components/ConvexChat.tsx (2)
138-149: Replace any with unknown and narrow error safelyDisallow any (C-10) and guard error.message access.
- } catch (error: any) { - console.error('[ConvexChat] Failed to create chat:', error); + } catch (error: unknown) { + console.error('[ConvexChat] Failed to create chat:', error); // Provide specific error messages - if (error.message?.includes('User must be authenticated')) { + const msg = error instanceof Error ? error.message : String(error); + if (msg.includes('User must be authenticated')) { setError('Authentication failed. Please sign out and sign back in.'); - } else if (error.message?.includes('ConvexError')) { + } else if (msg.includes('ConvexError')) { setError('Database connection failed. Check Convex configuration.'); } else { - setError(`Failed to create chat: ${error.message || 'Unknown error'}`); + setError(`Failed to create chat: ${msg || 'Unknown error'}`); }
205-208: Disallow any in delete handlerSame rationale; narrow error.
- } catch (error: any) { - console.error('Failed to delete chat:', error); + } catch (error: unknown) { + console.error('Failed to delete chat:', error);lib/groq-sequential-thinking.ts (1)
16-25: Replace any[] with a minimal SearchSource typeComply with C-10 and improve type safety for search results/sources.
export interface ThoughtStep { stepNumber: number; thought: string; confidence: number; searchQueries?: string[]; - searchResults?: any[]; + searchResults?: SearchSource[]; alternatives?: string[]; reasoning: string; timestamp: number; } export interface UltraThinkResponse { reasoningPaths: ReasoningPath[]; finalAnswer: string; confidence: number; - searchSources: any[]; + searchSources: SearchSource[]; processingTime: number; thoughtCount: number; ultraThinkMode: boolean; } export interface ReasoningProgress { type: 'thought_start' | 'thought_complete' | 'search_query' | 'search_results' | 'branch_created' | 'reasoning_complete'; step?: ThoughtStep; pathId?: string; searchQuery?: string; - searchResults?: any[]; + searchResults?: SearchSource[]; progress?: number; message?: string; }Add near the top of this file:
type SearchSource = { url?: string; title?: string; description?: string; [k: string]: unknown; };Also applies to: 49-56, 59-66
components/PricingModal.tsx (2)
46-52: Add dialog a11y: role, aria-modal, label; ensure button typeImprove screen-reader support and prevent accidental form submits.
- <motion.div + <motion.div initial={{ scale: 0.9, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} exit={{ scale: 0.9, opacity: 0 }} - className="bg-gray-900 rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto" + className="bg-gray-900 rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto" + role="dialog" + aria-modal="true" + aria-labelledby="pricing-modal-title" onClick={(e) => e.stopPropagation()} > <div className="p-6 border-b border-gray-700 flex items-center justify-between"> <div> - <h2 className="text-2xl font-bold text-white">Upgrade Your Plan</h2> + <h2 id="pricing-modal-title" className="text-2xl font-bold text-white">Upgrade Your Plan</h2> <p className="text-gray-400 mt-1">You've reached the free plan limit of 5 chats.</p> </div> - <button + <button + type="button" onClick={onClose} className="text-gray-400 hover:text-white transition-colors" aria-label="Close" >Also applies to: 55-56, 59-62
6-10: Tighten plan types: introduce PlanId and update props & handler signatures
- In components/PricingModal.tsx, change onUpgrade to onUpgrade: (plan: PlanId) => Promise (with
type PlanId = 'free' | 'pro').- In components/ConvexChat.tsx, update handleUpgrade to async (plan: PlanId) => Promise and branch on plan to select the correct priceId.
🧹 Nitpick comments (24)
.capy_commit_msg (1)
1-1: Clarify Pro price in the commit message.To avoid ambiguity, call out Pro as $20/mo (since Free is shown as 0/mo). Example: “Unify pricing to Free ($0) and Pro ($20/mo) …”.
.env.example (1)
26-27: Fix dotenv key ordering to satisfy dotenv-linter.Reorder the new key so it precedes NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY as suggested by the linter.
Apply this diff in the Stripe section:
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key_here STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key_here -# Stripe Pro price ID (fallback when Autumn isn't configured) -NEXT_PUBLIC_STRIPE_PRO_PRICE_ID=price_your_pro_monthly_price_id +# Stripe Pro price ID (fallback when Autumn isn't configured) +NEXT_PUBLIC_STRIPE_PRO_PRICE_ID=price_your_pro_monthly_price_idcomponents/stripe/SubscriptionPlans.tsx (1)
16-16: Use type-only React import for the icon type.Avoid namespace React types and follow type-only import guidance.
- icon?: React.ComponentType<{ className?: string }>; + import type { ComponentType } from 'react'; + // ... + icon?: ComponentType<{ className?: string }>;app/api/autumn/checkout/route.ts (2)
14-14: Avoidas any; type the Autumn ctx minimally.Keeps strict mode clean per guidelines.
- const mockCtx = { auth: { getUserIdentity: () => ({ subject: auth.userId, name: '', email: '' }) } } as any; + type AutumnAuthCtx = { auth: { getUserIdentity: () => { subject: string; name?: string; email?: string } } }; + const mockCtx: AutumnAuthCtx = { + auth: { getUserIdentity: () => ({ subject: auth.userId, name: '', email: '' }) } + };
18-22: Type the Autumn result instead ofany.- const result: any = await autumn.checkout(mockCtx, { priceId }); + type AutumnCheckoutResult = { url?: string; sessionUrl?: string; redirectUrl?: string }; + const result: AutumnCheckoutResult = await autumn.checkout(mockCtx, { priceId: resolvedPriceId });app/api/autumn/billing-portal/route.ts (3)
17-21: Avoidas anyand type the Autumn ctx/result.- const mockCtx = { auth: { getUserIdentity: () => ({ subject: auth.userId, name: '', email: '' }) } } as any; - const result: any = await autumn.billingPortal(mockCtx, { returnUrl: returnUrl || `${origin}/pricing` }); - const url = result?.url || result?.portalUrl || result?.redirectUrl; + type AutumnAuthCtx = { auth: { getUserIdentity: () => { subject: string; name?: string; email?: string } } }; + type AutumnPortalResult = { url?: string; portalUrl?: string; redirectUrl?: string }; + const mockCtx: AutumnAuthCtx = { + auth: { getUserIdentity: () => ({ subject: auth.userId, name: '', email: '' }) } + }; + const result: AutumnPortalResult = await autumn.billingPortal(mockCtx, { returnUrl: safeReturnUrl }); + const url = result.url || result.portalUrl || result.redirectUrl;
28-33: Add timeout to Stripe fallback and use the sanitized return URL.- const stripeRes = await fetch(`${origin}/api/stripe/customer-portal`, { + const stripeRes = await fetch(`${origin}/api/stripe/customer-portal`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ returnUrl: returnUrl || `${origin}/pricing` }) + body: JSON.stringify({ returnUrl: safeReturnUrl }), + signal: AbortSignal.timeout(10000) });
5-44: Deduplicate shared logic between Autumn routes.Auth extraction, context creation, origin handling, and fallbacks are duplicated here and in checkout. Consider a tiny internal util under app/api/autumn/_utils.ts.
I can draft a minimal shared helper if you want.
app/page.tsx (1)
28-28: Use a type-only import for Id.Aligns with coding guidelines and reduces runtime bundle.
-import { Id } from '@/convex/_generated/dataModel'; +import type { Id } from '@/convex/_generated/dataModel';components/ConvexChat.tsx (4)
6-6: Use a type-only import for IdFollow C-6 to avoid bundling type metadata into client code.
-import { Id } from '@/convex/_generated/dataModel'; +import type { Id } from '@/convex/_generated/dataModel';
45-46: Avoid logging PII in productionEmail can leak in prod logs. Gate logs under development.
- console.log('[ConvexChat] Status:', { isSignedIn, user: user?.emailAddresses?.[0]?.emailAddress, isConvexConfigured }); + if (process.env.NODE_ENV === 'development') { + console.log('[ConvexChat] Status:', { isSignedIn, user: user?.emailAddresses?.[0]?.emailAddress, isConvexConfigured }); + }
321-321: Prefer onKeyDown over onKeyPressonKeyPress is deprecated; use onKeyDown with key === 'Enter'.
Also applies to: 464-464
211-230: Deduplicate checkout logic with pricing pageSame checkout flow exists in app/pricing/page.tsx. Extract a shared helper (O-1) to lib/autumn/checkout.ts and reuse in both spots.
app/pricing/page.tsx (1)
7-28: Extract shared checkout helper to lib to avoid duplicationstartAutumnCheckout duplicates ConvexChat’s handleUpgrade. Extract to lib/autumn/checkout.ts and import in both files (O-1).
Proposed helper:
// lib/autumn/checkout.ts export async function startCheckout(getStripe: () => Promise<any>, priceId?: string) { const pid = priceId ?? process.env.NEXT_PUBLIC_STRIPE_PRO_PRICE_ID; if (!pid) throw new Error('Missing priceId'); const res = await fetch('/api/autumn/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ priceId: pid }) }); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); if (data.url) return window.location.assign(data.url); if (data.provider === 'stripe' && data.sessionId) { const stripe = await getStripe(); if (stripe) { const { error } = await stripe.redirectToCheckout({ sessionId: data.sessionId }); if (error) throw error; } } }lib/groq-sequential-thinking.ts (3)
293-308: Fix technical term extraction: capitalized pattern never matches due to lowercasingUse the original tokens for capitalization heuristics.
- private generateSearchQueries(thought: string, originalQuery: string): string[] { - const queries: string[] = []; - const words = thought.toLowerCase().split(/\W+/).filter(word => word.length > 3); - const importantWords = words.filter(word => !['that', 'this', 'with', 'from', 'they', 'have', 'been', 'will', 'would', 'could', 'should'].includes(word)); + private generateSearchQueries(thought: string, originalQuery: string): string[] { + const queries: string[] = []; + const wordsLower = thought.toLowerCase().split(/\W+/).filter(w => w.length > 3); + const importantWords = wordsLower.filter(w => !['that','this','with','from','they','have','been','will','would','could','should'].includes(w)); if (importantWords.length >= 2) { queries.push(`${originalQuery} ${importantWords.slice(0, 3).join(' ')}`); } - const technicalTerms = words.filter(word => word.length > 5 && (word.includes('tion') || word.includes('ment') || word.includes('ness') || word.includes('able') || word.match(/^[A-Z][a-z]+[A-Z]/))); + const originalWords = thought.split(/\W+/).filter(Boolean); + const technicalTerms = originalWords.filter(word => + word.length > 5 && + (/tion|ment|ness|able/i.test(word) || /^[A-Z][a-z]+[A-Z]/.test(word)) + ); if (technicalTerms.length > 0) { queries.push(`${technicalTerms[0]} ${originalQuery.split(' ').slice(0, 2).join(' ')}`); } return queries.slice(0, 2); }
136-136: Avoid deprecated substrUse slice for id generation.
- const pathId = `path_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + const pathId = `path_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
355-360: Emit reasoning_complete after final answer generationSignal completion for consumers of progress events.
private async generateFinalAnswer( @@ - let finalAnswer = ''; + let finalAnswer = ''; for await (const textPart of result.textStream) { finalAnswer += textPart; } - return finalAnswer; + progressCallback?.({ type: 'reasoning_complete', message: 'Final answer generated.' }); + return finalAnswer;Also applies to: 394-400
components/PricingModal.tsx (7)
12-14: AnimatePresence exit animation is bypassed by early returnReturning null when isOpen is false prevents exit animations. Render conditionally inside AnimatePresence instead.
export default function PricingModal({ isOpen, onClose, onUpgrade }: PricingModalProps) { - if (!isOpen) return null; ... - return ( - <AnimatePresence> - <motion.div + return ( + <AnimatePresence> + {isOpen && ( + <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4" onClick={onClose} > <motion.div initial={{ scale: 0.9, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} exit={{ scale: 0.9, opacity: 0 }} className="bg-gray-900 rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()} > ... </motion.div> </motion.div> + )} </AnimatePresence> ); }
15-24: De-dup the “5 chats” limit via a single constantAvoid drift between header copy and features.
- const plans: Plan[] = [ + const FREE_CHAT_LIMIT = 5 as const; + const plans: Plan[] = [ { id: 'free', name: 'Free', description: 'Perfect for getting started.', price: 0, features: [ - '5 chats', + `${FREE_CHAT_LIMIT} chats`, 'Basic models' ] },- <p className="text-gray-400 mt-1">You've reached the free plan limit of 5 chats.</p> + <p className="text-gray-400 mt-1">You've reached the free plan limit of {FREE_CHAT_LIMIT} chats.</p>Also applies to: 55-57
26-33: Remove unused “popular” flag or style itpopular is set but not used. Either drop it or use it for a badge/variant.
- popular: true,
81-84: Prefer locale-safe currency formattingSafer for future currency changes and avoids hardcoding "$".
- <span className="text-3xl font-bold text-white">${plan.price}</span> + <span className="text-3xl font-bold text-white"> + {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(plan.price)} + </span>
86-94: Disable “Current Plan” button instead of closing modalAvoids surprising close on Free; keep Pro as the only actionable CTA.
- <Button - onClick={() => plan.id === 'pro' ? onUpgrade(plan.id) : onClose()} + <Button + disabled={plan.name === 'Free'} + aria-disabled={plan.name === 'Free'} + onClick={plan.name === 'Pro' ? () => onUpgrade('pro') : undefined} className={`w-full ${ plan.name === 'Free' - ? 'bg-green-600 hover:bg-green-700' + ? 'bg-green-600 cursor-default opacity-60' : 'bg-blue-600 hover:bg-blue-700' }`} > {plan.name === 'Free' ? 'Current Plan' : 'Upgrade'} </Button>
98-105: Use stable keys for featuresUse the feature string as the key to avoid index-key pitfalls on reorders.
- {plan.features.map((feature, index) => ( - <div key={index} className="flex items-start gap-3"> + {plan.features.map((feature) => ( + <div key={feature} className="flex items-start gap-3">
39-45: Optional: support Escape to close and focus managementConsider adding an onKeyDown handler (with tabIndex={-1}) to close on Escape, and a focus trap to keep focus within the dialog while open. I can draft a minimal FocusTrap if helpful.
Also applies to: 46-51
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
.capy_commit_msg(1 hunks).env.example(1 hunks)AutumnWrapper.tsx(1 hunks)app/api/autumn/billing-portal/route.ts(1 hunks)app/api/autumn/checkout/route.ts(1 hunks)app/components/AutumnFallback.tsx(3 hunks)app/page.tsx(4 hunks)app/pricing/page.tsx(3 hunks)app/test-pricing/page.tsx(8 hunks)components/ConvexChat.tsx(1 hunks)components/EnhancedSettingsModal.tsx(1 hunks)components/MasterDashboard.tsx(3 hunks)components/PricingModal.tsx(5 hunks)components/stripe/SubscriptionPlans.tsx(6 hunks)lib/groq-sequential-thinking.ts(9 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Disallowanytypes; useunknown, unions, or generics instead
Use branded types for IDs (e.g.,type SandboxId = Brand<string, 'SandboxId'>) across the codebase
Use type-only imports:import type { ... }for types
Default totypeoverinterfaceunless declaration merging is needed
Avoid comments except for critical caveats; write self-explanatory code
Prefer simple, composable functions over classes
Don't extract functions unless reused or for testability
Use branded types for external service IDs (SandboxId, ChatId, UserId) throughout the codebase
**/*.{ts,tsx}: C-1: Follow TDD (scaffold stub → write failing test → implement)
C-2: Name functions with established domain vocabulary for consistency
C-3: Avoid introducing classes when small testable functions suffice
C-4: Prefer simple, composable, testable functions
C-5: Prefer branded types for IDs (e.g., type SandboxId = Brand<string,'SandboxId'>)
C-6: Use import type { … } for type-only imports
C-7: Avoid comments except for critical caveats; rely on self-explanatory code
C-8: Default to type; use interface only when clearer or for interface merging
C-9: Don’t extract a new function unless reused, enables testing otherwise untestable logic, or drastically improves readability
C-10: Do not use any; use unknown, proper unions, or generics instead
D-4: Use branded types for external service IDs (E2B sandbox IDs, Convex document IDs)
G-2: TypeScript compilation must pass with strict mode
Writing Functions: Keep cyclomatic complexity low and code easy to follow
Writing Functions: Remove unused parameters
Writing Functions: Avoid unnecessary type casts; push casting to function boundaries
Writing Functions: Make functions easily testable without mocking core services; otherwise cover via integration tests
Writing Functions: Factor out hidden dependencies into arguments when they affect behavior
Writing Functions: Choose clear, consistent names; brainstorm alternatives
Writing Functions: Do n...
Files:
app/pricing/page.tsxcomponents/ConvexChat.tsxAutumnWrapper.tsxcomponents/EnhancedSettingsModal.tsxapp/api/autumn/billing-portal/route.tsapp/api/autumn/checkout/route.tsapp/page.tsxlib/groq-sequential-thinking.tsapp/test-pricing/page.tsxcomponents/MasterDashboard.tsxcomponents/PricingModal.tsxcomponents/stripe/SubscriptionPlans.tsxapp/components/AutumnFallback.tsx
app/api/**
📄 CodeRabbit inference engine (.cursorrules)
Group related routes in subdirectories
Files:
app/api/autumn/billing-portal/route.tsapp/api/autumn/checkout/route.ts
app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
O-2: Keep API route logic in app/api with clear separation between sandbox management, AI processing, and autonomous workflows
Files:
app/api/autumn/billing-portal/route.tsapp/api/autumn/checkout/route.ts
lib/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
O-1: Place shared utilities in lib/ only if used by two or more components or API routes
Files:
lib/groq-sequential-thinking.ts
🧠 Learnings (4)
📚 Learning: 2025-09-09T03:08:55.499Z
Learnt from: CR
PR: otdoges/zapdev#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-09T03:08:55.499Z
Learning: Applies to app/components/chat/**/*.{ts,tsx} : UI Components: app/components/chat contains chat and AI interaction components
Applied to files:
components/ConvexChat.tsx
📚 Learning: 2025-09-09T03:08:55.499Z
Learnt from: CR
PR: otdoges/zapdev#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-09T03:08:55.499Z
Learning: Applies to convex/chats.ts : Convex Schema: convex/chats.ts manages chat history and conversations
Applied to files:
app/page.tsx
📚 Learning: 2025-09-09T03:08:55.499Z
Learnt from: CR
PR: otdoges/zapdev#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-09T03:08:55.499Z
Learning: Applies to lib/ai/**/*.{ts,tsx} : Core Libraries: lib/ai contains multi-provider AI client wrappers and streaming utilities
Applied to files:
lib/groq-sequential-thinking.ts
📚 Learning: 2025-09-09T03:08:55.499Z
Learnt from: CR
PR: otdoges/zapdev#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-09T03:08:55.499Z
Learning: Applies to app/components/monitoring/**/*.{ts,tsx} : UI Components: app/components/monitoring contains dashboard and monitoring UI
Applied to files:
components/MasterDashboard.tsx
🧬 Code graph analysis (8)
app/pricing/page.tsx (2)
lib/stripe.ts (1)
stripe(7-9)lib/stripe-client.ts (1)
getStripe(9-14)
components/ConvexChat.tsx (2)
lib/stripe.ts (1)
stripe(7-9)lib/stripe-client.ts (1)
getStripe(9-14)
app/api/autumn/billing-portal/route.ts (1)
app/api/autumn/checkout/route.ts (1)
POST(5-49)
app/api/autumn/checkout/route.ts (1)
app/api/autumn/billing-portal/route.ts (1)
POST(5-45)
app/page.tsx (1)
convex/chats.ts (1)
updateChatScreenshot(325-362)
lib/groq-sequential-thinking.ts (3)
lib/mcp/sequential-thinking-service.ts (1)
getSequentialThinkingService(465-470)lib/search/reasoning-search.ts (1)
getReasoningSearchService(496-501)config/app.config.ts (1)
appConfig(4-177)
app/test-pricing/page.tsx (1)
components/stripe/CheckoutButton.tsx (1)
CheckoutButton(28-113)
components/PricingModal.tsx (1)
components/ui/button.tsx (1)
Button(52-52)
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 27-27: [UnorderedKey] The NEXT_PUBLIC_STRIPE_PRO_PRICE_ID key should go before the NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY key
(UnorderedKey)
🔇 Additional comments (15)
AutumnWrapper.tsx (1)
2-2: No changes required; named export matches upstream
Confirmed thatAutumnWrapper.tsxexportsAutumnWrapperas a named function, soexport { AutumnWrapper } from "./app/components/AutumnWrapper";is correct.components/stripe/SubscriptionPlans.tsx (5)
3-3: Icon import change is fine.No issues with the reduced lucide-react imports.
35-37: Feature list trim looks good.Copy aligned with Free plan constraints.
53-53: Copy change acknowledged.“Advanced models” matches the PR scope.
74-74: Grid tweak OK.Two-card layout matches “Free & Pro”.
153-153: No change needed – CheckoutButton expects amount in smallest currency unit (cents), soplan.price * 100is correct.app/api/autumn/checkout/route.ts (1)
44-46: Frontend covers both{url}and{provider, sessionId}shapes. ConvexChat and Pricing pages first checkdata.url(stripe API), thendata.provider === 'stripe' && data.sessionId(autumn proxy), so no changes needed.app/components/AutumnFallback.tsx (2)
75-107: Two-tier fallback pricing UX looks consistent with the new plans.Copy, price ($20), features, and 2-column layout align with the PR objectives. No functional concerns here.
50-53: Confirm time unit for currentPeriodEnd.If downstream expects Unix seconds (Stripe-style), this ms value will be off by 1000x. Verify consumers before shipping.
Would you like a quick scan to find usages of subscription.currentPeriodEnd and confirm the expected unit?
app/test-pricing/page.tsx (5)
23-49: Test plans updated to Free/Pro look good.Env-driven Pro priceId with a safe fallback is a solid default; feature lists match the simplified offering.
136-148: Correct cents conversion for customAmount.Rounding to integer cents avoids Stripe validation errors. LGTM.
165-191: Quick test scenarios updated.Amounts and labels are coherent; good coverage of payment vs subscription paths.
Also applies to: 195-205
220-224: Test cards list is accurate and concise.Covers success, decline, insufficient funds, and 3DS flows. Good for demoing error paths.
43-44: NEXT_PUBLIC_STRIPE_PRO_PRICE_ID is documented and wired. .env.example declares NEXT_PUBLIC_STRIPE_PRO_PRICE_ID, and all code references use that variable rather than falling back in production.components/PricingModal.tsx (1)
112-114: Copy LGTM“Cancel anytime.” is succinct and consistent with the simplified pricing narrative.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
app/components/AutumnFallback.tsx (6)
4-7: Prefer type-only imports and avoid React.FC per guidelinesDrop the default React import and use type-only imports; also prefer function components over React.FC.
-import React, { createContext, useContext, useState, useEffect } from 'react'; +import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';Outside this hunk, update component declarations accordingly:
// before export const AutumnProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { /*...*/ } export const PricingTable: React.FC = () => { /*...*/ } // after export const AutumnProvider = ({ children }: { children: ReactNode }) => { /*...*/ } export const PricingTable = () => { /*...*/ }
38-41: Remove unused state setter to satisfy no-unused-vars
setErroris never used.- const [error, setError] = useState<string | null>(null); + const [error] = useState<string | null>(null);
82-87: Free plan copy/limits — confirm accuracy and simplify periodEnsure quotas match the new cost controls, and consider omitting the “period” label for Free to avoid odd “forever” wording.
- period: 'forever', + period: undefined,
92-98: Pro plan copy: reflect Kimi K2 and pricing styleMatch $20/mo wording and call out Kimi K2 explicitly.
- period: 'per month', - description: 'For builders who want more', - features: ['Unlimited chats', 'Advanced models'], + period: '/mo', + description: 'For builders who want more', + features: ['Unlimited chats', 'Kimi K2 models'],
101-134: Wire the CTA to Autumn checkout (fallback should still upgrade)Hook the Pro button to POST /api/autumn/checkout and redirect to the returned URL.
- <Button className="w-full" variant={plan.popular ? 'default' : 'outline'}> + <Button + className="w-full" + variant={plan.popular ? 'default' : 'outline'} + onClick={plan.id === 'pro' ? () => startAutumnCheckout() : undefined} + > {plan.buttonText} </Button>Add this helper near the top of the file (outside this hunk):
async function startAutumnCheckout() { try { const res = await fetch('/api/autumn/checkout', { method: 'POST' }); const data = await res.json(); if (data?.url) window.location.href = data.url; } catch { // noop fallback } }
155-161: useUsageLimits: type mutate and align with branded IDs
- Return a Promise from mutate to better mirror SWR-style signatures.
- Consider branding
featureId(e.g.,type FeatureId = Brand<string, 'FeatureId'>) per guidelines.return { data: limits, isLoading, - mutate: () => {}, + mutate: async () => {}, isValidating: false };If a shared
Brandtype exists in the repo, switch the hook signature touseUsageLimits(featureId: FeatureId).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
app/components/AutumnFallback.tsx(3 hunks)app/pricing/page.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/pricing/page.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Disallowanytypes; useunknown, unions, or generics instead
Use branded types for IDs (e.g.,type SandboxId = Brand<string, 'SandboxId'>) across the codebase
Use type-only imports:import type { ... }for types
Default totypeoverinterfaceunless declaration merging is needed
Avoid comments except for critical caveats; write self-explanatory code
Prefer simple, composable functions over classes
Don't extract functions unless reused or for testability
Use branded types for external service IDs (SandboxId, ChatId, UserId) throughout the codebase
**/*.{ts,tsx}: C-1: Follow TDD (scaffold stub → write failing test → implement)
C-2: Name functions with established domain vocabulary for consistency
C-3: Avoid introducing classes when small testable functions suffice
C-4: Prefer simple, composable, testable functions
C-5: Prefer branded types for IDs (e.g., type SandboxId = Brand<string,'SandboxId'>)
C-6: Use import type { … } for type-only imports
C-7: Avoid comments except for critical caveats; rely on self-explanatory code
C-8: Default to type; use interface only when clearer or for interface merging
C-9: Don’t extract a new function unless reused, enables testing otherwise untestable logic, or drastically improves readability
C-10: Do not use any; use unknown, proper unions, or generics instead
D-4: Use branded types for external service IDs (E2B sandbox IDs, Convex document IDs)
G-2: TypeScript compilation must pass with strict mode
Writing Functions: Keep cyclomatic complexity low and code easy to follow
Writing Functions: Remove unused parameters
Writing Functions: Avoid unnecessary type casts; push casting to function boundaries
Writing Functions: Make functions easily testable without mocking core services; otherwise cover via integration tests
Writing Functions: Factor out hidden dependencies into arguments when they affect behavior
Writing Functions: Choose clear, consistent names; brainstorm alternatives
Writing Functions: Do n...
Files:
app/components/AutumnFallback.tsx
Summary
Key changes
Autumn wrapper
Acceptance notes
Security
Testing
₍ᐢ•(ܫ)•ᐢ₎ Generated by Capy (view task)
Summary by CodeRabbit
New Features
Style
Refactor
Chores