Skip to content

feat: Implement core project management, AI conversation, and code ed… - #14

Open
Mayank-saraswal wants to merge 2 commits into
mainfrom
big-change
Open

feat: Implement core project management, AI conversation, and code ed…#14
Mayank-saraswal wants to merge 2 commits into
mainfrom
big-change

Conversation

@Mayank-saraswal

@Mayank-saraswal Mayank-saraswal commented Feb 19, 2026

Copy link
Copy Markdown
Owner

…iting features with associated APIs and UI components.

Summary by CodeRabbit

  • New Features
    • Full billing flow: subscriptions, one-time token purchases, usage API, webhook handling, and a pricing page with checkout
    • Usage dashboard showing plan, token balance, per-action/model breakdown and 30-day trends
    • File and conversation REST APIs, real-time conversation updates, and E2B sandbox for running code
  • Refactor
    • Backend migrated to Prisma/PostgreSQL and Azure Blob storage; auth unified to Clerk; per-user rate limits added
    • Client hooks and components updated to use string IDs and REST endpoints
  • Documentation
    • Added comprehensive setup guide for external service keys and quick-start checklist
  • Chores
    • Updated dependencies for database, storage, billing, realtime, and caching integrations

…iting features with associated APIs and UI components.
@vercel

vercel Bot commented Feb 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moris Error Error Feb 25, 2026 5:55am

@coderabbitai

coderabbitai Bot commented Feb 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request migrates the backend from Convex to a Prisma/Postgres + Next.js API routes architecture, adds Razorpay billing and token/token-economy management, introduces Redis rate limiting and caching, integrates Azure Blob Storage for file blobs, implements E2B sandbox utilities, and updates frontend hooks/components to use string IDs with React Query and Supabase realtime where needed.

Changes

Cohort / File(s) Summary
Docs
README-API-KEYS.md
New onboarding guide for external services and .env setup.
Convex removal
convex/* (convex/auth.config.ts, convex/*.ts, convex/schema.ts)
Removed Convex config, schema, and all Convex RPC surfaces.
Prisma & DB
prisma/schema.prisma, prisma.config.ts, src/lib/prisma.ts
Add Postgres schema and Prisma client singleton; DB models for projects, files, conversations, messages, subscriptions, token usage/purchases.
API routes (billing)
src/app/api/billing/*
Razorpay purchase/subscribe/usage/webhook endpoints and billing logic.
API routes (projects/conversations/files/messages/github)
src/app/api/projects/*, src/app/api/conversations/*, src/app/api/files/*, src/app/api/messages/*, src/app/api/github/*
New Next.js REST handlers backed by Prisma for projects, conversations, files (with Azure blob ops), messages (rate-limited + token checks), and GitHub import/export flows.
File storage & Azure
src/lib/azure-storage.ts, src/lib/file-storage.ts
Azure Blob client wrapper and high-level file storage helpers (upload/download/delete, mime handling).
Rate limiting & cache
src/lib/redis.ts
Upstash Redis client, rate limiter factory, predefined limiters, and cache helpers.
Razorpay & pricing
src/lib/razorpay.ts
Razorpay client helper, plans/pricing, webhook signature verification.
Token economics
src/lib/tokens.ts
Token multiplier/pricing, balance checks, deductTokens, upgrade/reset flows with Redis cache.
E2B sandbox
src/lib/e2b-sandbox.ts, src/types/e2b.d.ts
Sandbox lifecycle and runProject helpers; ambient types for e2b.
Frontend data layer (hooks)
src/features/*/hooks/* (use-projects, use-files, use-conversations, use-billing)
Rewrote hooks to use REST endpoints + React Query; new types (Project, FileItem, Conversation, Message, UsageData); file content hook and CRUD mutations.
Inngest tools & process-message
src/features/conversations/inngest/*
Converted tools and process-message to Prisma + file-storage, switched to path-based file APIs (parentPath), updated event types to string IDs, added token deduction integration.
Frontend components/pages
src/components/providers.tsx, src/app/*, src/features/*/components/*
Replaced Convex providers with Clerk + QueryClient; updated components/pages (dashboard, pricing) and many components to accept string IDs and use new hooks.
Editor & preview
src/features/editor/*, src/features/preview/*
Editor store/hooks and preview file-tree migrated to path-based FileRecord/FileItem; useFileContent added; IDs changed to string.
Dependencies & config
package.json, tsconfig.json, src/lib/convex-client.ts
Removed convex dependency and client; added prisma, @prisma/client, pg adapter, Upstash, Razorpay, Azure SDK, Supabase, React Query, etc.; ts target bumped to ES2020.
Agents/Skills & assets
.agents/skills/*, skills-lock.json
New skill docs and license files; skills lock manifest.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Client
    participant APIRoute as POST /api/messages
    participant Auth as Clerk Auth
    participant RateLimit as Redis RateLimiter
    participant TokenCheck as checkTokenBalance
    participant Prisma as Prisma Client
    participant Inngest as Inngest Event
    participant ProcessMsg as process-message Handler
    participant TokenDeduct as deductTokens

    User->>APIRoute: Send message with conversationId
    APIRoute->>Auth: Verify user identity
    Auth-->>APIRoute: UserId confirmed
    APIRoute->>RateLimit: Check message rate limit
    alt Rate limit exceeded
        RateLimit-->>APIRoute: 429 Too Many Requests
        APIRoute-->>User: Error response
    else Rate limit OK
        RateLimit-->>APIRoute: Limit ok
        APIRoute->>TokenCheck: Check token balance
        alt Insufficient tokens
            TokenCheck-->>APIRoute: insufficient
            APIRoute-->>User: 402 Payment Required
        else Tokens available
            TokenCheck-->>APIRoute: ok
            APIRoute->>Prisma: Validate conversation ownership
            Prisma-->>APIRoute: conversation OK
            APIRoute->>Prisma: Create user message & assistant placeholder
            Prisma-->>APIRoute: message IDs
            APIRoute->>Inngest: Trigger process-message (with messageId)
            APIRoute-->>User: 200 Created
            Inngest->>ProcessMsg: Execute handler
            ProcessMsg->>Prisma: Fetch context & messages
            ProcessMsg->>ProcessMsg: Run AI + tools (file ops, e2b, etc.)
            ProcessMsg->>Prisma: Update assistant message status/content
            ProcessMsg->>TokenDeduct: Deduct tokens and log usage
            TokenDeduct->>Prisma: Update subscription/usage
            ProcessMsg-->>Inngest: Complete
        end
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped from Convex fields to Prisma rows,
I buried blobs in Azure where the river flows,
Counted tokens, paced the hops with Redis cheer,
Rang Razorpay bells when the rabbit's purse was near,
Tiny paws, big migration — carrots for the engineers! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: Implement core project management, AI conversation, and code ed…' is truncated and accurately summarizes the main changes: migration from Convex to Prisma/PostgreSQL with new API routes, billing system, and UI components for project management, conversations, and code editing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch big-change

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
src/features/preview/components/preview-settings-popover.tsx (1)

48-58: ⚠️ Potential issue | 🟠 Major

Mutation errors are silently swallowed — user receives no feedback on failure

When mutateAsync rejects (network error, API error, etc.), TanStack Form's internal handleSubmit wrapper catches the exception, resets isSubmitting to false, and re-enables the Save button — with no visible indication of what went wrong. setOpen and onSave are already correctly skipped, but the user is left staring at the same open popover with no error message.

🔒️ Proposed fix: wrap mutation in try/catch and surface the error
         onSubmit: async ({ value }) => {
-            await updateSettings.mutateAsync({
-                id: projectId,
-                settings: {
-                    installCommand: value.installCommand || undefined,
-                    devCommand: value.devCommand || undefined,
-                },
-            });
-            setOpen(false);
-            onSave?.();
+            try {
+                await updateSettings.mutateAsync({
+                    id: projectId,
+                    settings: {
+                        installCommand: value.installCommand || undefined,
+                        devCommand: value.devCommand || undefined,
+                    },
+                });
+                setOpen(false);
+                onSave?.();
+            } catch (error) {
+                // Surface the error via your app's notification system, e.g.:
+                // toast.error("Failed to save settings. Please try again.");
+                console.error("Failed to update project settings:", error);
+            }
         }

Alternatively, read updateSettings.isError / updateSettings.error from the mutation object and render an inline error below the submit button — this avoids local try/catch and integrates naturally with TanStack Query's state model.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/preview/components/preview-settings-popover.tsx` around lines 48
- 58, The submit handler currently calls updateSettings.mutateAsync and then
closes the popover (setOpen) and calls onSave, but any rejection is swallowed —
wrap the mutateAsync call in a try/catch (or alternatively rely on
updateSettings.isError/updateSettings.error) and on error set a visible error
state (e.g., local submitError or use form.setError) and render that message
below the Save button; only call setOpen and onSave on success so failures
surface to the user (reference updateSettings.mutateAsync, setOpen, onSave in
the preview-settings-popover component).
src/features/projects/components/import-github-dialog.tsx (1)

63-89: ⚠️ Potential issue | 🟠 Major

error.response.json() can throw inside the catch block, swallowing the fallback toast

If the server returns a non-JSON error response (e.g., a 502/504 with an HTML body, an empty body, or a network-level error page), error.response.json() throws a SyntaxError. Because this occurs inside the catch block, the exception propagates out of onSubmit as an unhandled rejection — the fallback toast.error(...) on line 88 is never reached, and the form may remain stuck in isSubmitting state.

🛡️ Proposed fix
     } catch (error) {
       if (error instanceof HTTPError) {
-        const body = await error.response.json<{ error: string }>();
-        if (body.error?.includes("Pro plan required")) {
-          toast.error("Upgrade to import repositories", {
-            action: {
-              label: "Upgrade",
-              onClick: () => openUserProfile(),
-            },
-          });
-          onOpenChange(false);
-          return;
-        }
-
-        if (body.error?.includes("GitHub not connected")) {
-          toast.error("GitHub account not connected", {
-            action: {
-              label: "Connect",
-              onClick: () => openUserProfile(),
-            },
-          });
-          onOpenChange(false);
-          return;
-        }
+        try {
+          const body = await error.response.json<{ error: string }>();
+          if (body.error?.includes("Pro plan required")) {
+            toast.error("Upgrade to import repositories", {
+              action: { label: "Upgrade", onClick: () => openUserProfile() },
+            });
+            onOpenChange(false);
+            return;
+          }
+          if (body.error?.includes("GitHub not connected")) {
+            toast.error("GitHub account not connected", {
+              action: { label: "Connect", onClick: () => openUserProfile() },
+            });
+            onOpenChange(false);
+            return;
+          }
+        } catch {
+          // Non-JSON response body; fall through to generic error toast
+        }
       }
       toast.error("Unable to import repository. Please check the URL and try again");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/import-github-dialog.tsx` around lines 63 -
89, The catch block in onSubmit assumes error.response.json() always succeeds
(checking instanceof HTTPError and reading body), but JSON parsing can throw and
bypass the fallback toast; update the catch to safely parse the response (wrap
error.response.json() in its own try/catch or use a safe parser that falls back
to text) so parse failures do not escape the outer catch, then inspect the
parsed body/error string for "Pro plan required" or "GitHub not connected" and
only after those checks call the fallback toast.error("Unable to import
repository..."); keep existing calls to onOpenChange(false) and
openUserProfile() as currently used when matching those messages.
src/features/projects/components/export-popover.tsx (2)

56-66: ⚠️ Potential issue | 🟠 Major

repoName default value won't populate when project data loads asynchronously.

useForm evaluates defaultValues synchronously at initialization. If useProject(projectId) hasn't resolved yet (cache miss), project is undefined and repoName initializes to "". When React Query later sets project, the form state is not updated.

✏️ Proposed fix — reset the form when project data arrives
+import React, { useEffect } from "react";
 // ...

 const form = useForm({
     defaultValues: {
         repoName: project?.name?.replace(/[^a-zA-Z0-9._-]/g, "-") ?? "",
         visibility: "private" as "public" | "private",
         description: "",
     },
     // ...
 });
+
+useEffect(() => {
+    if (project?.name) {
+        form.setFieldValue(
+            "repoName",
+            project.name.replace(/[^a-zA-Z0-9._-]/g, "-")
+        );
+    }
+}, [project?.name]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/export-popover.tsx` around lines 56 - 66,
The form's repoName default is set from project at initialization so it stays
empty if project loads later; update the form when project data arrives by
adding an effect that watches project and calls the form API (e.g., form.reset
or form.setValue) to set repoName to project?.name?.replace(/[^a-zA-Z0-9._-]/g,
"-") and any other fields you want to initialize; reference
useProject(projectId) to detect the loaded project and use the form instance
returned from useForm to perform the update.

8-14: ⚠️ Potential issue | 🟡 Minor

Dead code: unused imports and unreachable handlers left over from removed export-status feature.

  • CheckCheckIcon, CheckCircle2Icon, ExternalLinkIcon, LoaderIcon, XCircleIcon are imported but never referenced in the simplified render path.
  • handleCancelExport (line 112) and handleResetExport (line 118) are defined but never called — these were part of the removed exporting/completed/failed status UI.

Remove all five unused imports and both handlers to avoid dead code.

Also applies to: 112-123

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/export-popover.tsx` around lines 8 - 14,
Remove the dead code left from the old export-status UI: delete the unused icon
imports CheckCheckIcon, CheckCircle2Icon, ExternalLinkIcon, LoaderIcon,
XCircleIcon from the import list in export-popover.tsx, and remove the two
unused handler functions handleCancelExport and handleResetExport (and any
related unused local variables) so there are no unreachable or unreferenced
symbols left in the component.
src/features/conversations/inngest/constants.ts (1)

7-13: ⚠️ Potential issue | 🟡 Minor

Stale "IDs" references in workflow steps conflict with the new parentPath-based rules.

Lines 7 and 10 still instruct the agent to "Note the IDs of folders" and "get their IDs", but lines 17–18 now direct it to use parentPath instead. This creates contradictory guidance within the same prompt.

✏️ Proposed fix
-1. Call listFiles to see the current project structure. Note the IDs of folders you need.
+1. Call listFiles to see the current project structure. Note the paths of folders you need.
 2. Call readFiles to understand existing code when relevant.
 3. Execute ALL necessary changes:
-   - Create folders first to get their IDs
+   - Create folders first to determine their paths
    - Use createFiles to batch create multiple files in the same folder (more efficient)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/conversations/inngest/constants.ts` around lines 7 - 13, Update
the stale instructions that reference "IDs" to consistently use the new
parentPath-based convention: replace the phrases "Note the IDs of folders" and
"get their IDs" with wording like "note the parentPath of folders" or "use
parentPath" so steps 1 and 3 no longer conflict with the lines that already
reference parentPath; ensure the strings "Note the IDs of folders" and "get
their IDs" are updated to reference "parentPath" and keep the rest of the
workflow steps intact so all guidance consistently uses parentPath-based rules.
src/features/projects/components/project-id-view.tsx (1)

50-50: ⚠️ Potential issue | 🟡 Minor

Dead code — useProject called but result never used, triggering an unnecessary network request.

project stores the full QueryObserverResult object (not the project data), and nothing in the component's JSX references it. This fires a redundant /api/projects/:id fetch on every render. Remove the call (or destructure { data: project } if project data is needed in this component in the future).

🛠️ Proposed fix
-    const project = useProject(projectId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/project-id-view.tsx` at line 50, The call to
useProject(projectId) returns a QueryObserverResult saved to the variable
project but its value is never used, causing an unnecessary API fetch; either
delete the unused line "const project = useProject(projectId);" to stop the
request, or replace it with a destructure that only grabs data when needed
(e.g., "const { data: project } = useProject(projectId);" or pass an options
object to disable fetching until required) so the component no longer triggers
the redundant /api/projects/:id call.
src/app/api/github/import/route.ts (1)

27-28: ⚠️ Potential issue | 🟠 Major

requestSchema.parse(body) throws unhandled ZodError on invalid URL

Same issue as the export route — use safeParse and return a 400.

🛠️ Proposed fix
-    const { url } = requestSchema.parse(body);
+    const parsed = requestSchema.safeParse(body);
+    if (!parsed.success) {
+        return NextResponse.json(
+            { error: parsed.error.issues[0]?.message ?? "Invalid request" },
+            { status: 400 }
+        );
+    }
+    const { url } = parsed.data;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/github/import/route.ts` around lines 27 - 28, Replace the
unhandled requestSchema.parse call with requestSchema.safeParse(body) in the
import route handler: call const result = requestSchema.safeParse(body), check
result.success, and if false return a 400 response (including the validation
error message or a concise error payload) instead of letting ZodError bubble;
otherwise use result.data.url for the rest of the logic (this mirrors the export
route fix and prevents unhandled ZodError crashes).
src/app/api/github/export/route.ts (1)

39-48: ⚠️ Potential issue | 🟠 Major

GitHub OAuth token passed plaintext in Inngest event payload — will be stored in event logs

Inngest persists all event data to its event history/log storage, accessible from the dashboard and API. Including githubToken here exposes a live OAuth credential to anyone with Inngest dashboard access and to any Inngest log storage backend.

The token should be fetched at job execution time inside the Inngest function handler instead of being passed as event data. The userId already present in the event is sufficient to re-fetch the token from Clerk at execution time.

🛠️ Proposed fix
     const event = await inngest.send({
         name: "github/export.repo",
         data: {
             projectId,
             repoName,
             visibility,
             description,
-            githubToken,
         },
     });

In the Inngest handler, re-fetch the token using userId from event metadata or pass userId in the event instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/github/export/route.ts` around lines 39 - 48, The event payload
currently includes the plaintext githubToken (see inngest.send call and data
object with githubToken) which will be persisted; remove githubToken from the
data sent to Inngest and instead include the caller's userId (or ensure userId
is present in event metadata), then in the Inngest function handler re-fetch the
GitHub OAuth token using Clerk (or your token store) keyed by userId at
execution time (update the handler that processes "github/export.repo" to
retrieve the token from Clerk using userId and use that token for API calls).
src/app/api/projects/create-with-prompt/route.ts (1)

36-37: ⚠️ Potential issue | 🟠 Major

Unhandled request.json() and Zod parse errors will produce raw 500 responses.

Neither the JSON parsing nor the Zod validation is wrapped in error handling. A malformed body or missing prompt will throw an unstructured error. Consider wrapping in try-catch and returning a 400 with validation details.

Proposed fix
+    let prompt: string;
+    try {
+        const body = await request.json();
+        ({ prompt } = requestSchema.parse(body));
+    } catch {
+        return NextResponse.json({ error: "Invalid request body" }, { status: 400 });
+    }
-    const body = await request.json();
-    const { prompt } = requestSchema.parse(body);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/projects/create-with-prompt/route.ts` around lines 36 - 37, Wrap
the JSON parsing and Zod validation in a try-catch inside the route handler so
malformed JSON or validation failures don't bubble up as 500s; call await
request.json() and requestSchema.parse(body) inside the try, catch SyntaxError
(or generic error from request.json) and ZodError from requestSchema.parse, and
return a 400 response containing the validation/parsing details (e.g.,
error.message or ZodError.format()) instead of throwing. Ensure the catch
branches reference the same symbols (request.json and requestSchema.parse) so
the handler returns structured 400 responses for bad input.
src/features/projects/inngest/import-to-github-repo.ts (1)

94-147: ⚠️ Potential issue | 🟠 Major

All file fetches, uploads, and DB writes run inside a single Inngest step — timeout risk for large repos.

The "create-files" step iterates over every file in the repo, performing a GitHub API call, a Prisma create, an Azure upload, and a Prisma update per file — all within one step. For repositories with hundreds or thousands of files, this will likely exceed the Inngest step execution time limit.

Consider batching files into multiple steps (e.g., chunks of 20-50) using step.run per batch, which also gives you automatic retries per batch rather than restarting the entire file import on failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/inngest/import-to-github-repo.ts` around lines 94 -
147, The "create-files" Inngest step currently processes allFiles in a single
step (inside step.run("create-files")), causing timeout risk; change the
implementation to split allFiles into batches (e.g., chunks of 20–50) and call
step.run for each batch (e.g., step.run(`create-files-${batchIndex}`)) so each
batch fetches blobs via octokit.rest.git.getBlob, creates records via
prisma.file.create, uploads via uploadBinaryFile/uploadTextFile, and updates via
prisma.file.update within its own step; this gives per-batch retries and keeps
each step short—ensure you reference the existing symbols allFiles,
step.run("create-files"), uploadBinaryFile, uploadTextFile, prisma.file.create,
and prisma.file.update when locating and refactoring the code.
src/features/projects/inngest/export-to-github.ts (1)

153-160: ⚠️ Potential issue | 🟠 Major

Tree creation without base_tree discards the auto-init README.

auto_init: true creates a README.md in the initial commit, but createTree is called without base_tree. This creates a new tree containing only the exported files, which replaces the initial tree when main is updated. The README.md will not appear in the exported repository. If preserving the README is desired, use base_tree set to the initial commit SHA, or include README.md in the tree items before creation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/inngest/export-to-github.ts` around lines 153 - 160,
createTree is being called without a base_tree which discards the repo’s
auto_init README; update the create-tree step (the octokit.rest.git.createTree
call that uses treeItems) to either pass base_tree set to the initial commit SHA
(fetch the initial commit SHA beforehand) or ensure README.md is included in
treeItems so the README is preserved in the resulting tree before updating refs.
src/features/projects/components/projects-list.tsx (1)

37-59: ⚠️ Potential issue | 🟠 Major

Nested interactive elements: <Button> wrapping <Link>.

A <button> containing an <a> (rendered by <Link>) is invalid HTML and creates accessibility problems — screen readers see two interactive elements, and keyboard focus/activation behavior becomes ambiguous. Use <Button asChild> to merge the Button into the Link element.

Fix: use asChild prop
             <Button
                 variant="outline"
                 className="h-auto items-start justify-start p-4 bg-background border rounded-none flex flex-col gap-2"
+                asChild
             >
                 <Link href={`/projects/${data.id}`} className="group">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/projects-list.tsx` around lines 37 - 59, The
Button currently wraps a Link (creating nested interactive elements); change
Button to use the asChild prop so the Link becomes the rendered element with
Button styling: add asChild to the <Button> declaration and keep the <Link
href={`/projects/${data.id}`} ...> as the direct child (preserving the inner
structure including getProjectIcon(data), {data.name}, ArrowRightIcon and the
timestamp formatting via formatTimestamp(data.updatedAt)), and ensure any
Button-specific className/variant are applied to the Button so they flow through
to the Link child.

Comment thread prisma.config.ts
@@ -0,0 +1,12 @@
import path from "node:path";
import dotenv from "dotenv";
import { defineConfig } from "@prisma/config";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "prisma.config.ts" -o -name "prisma.config.js" 2>/dev/null

Repository: Mayank-saraswal/moris

Length of output: 83


🏁 Script executed:

cat package.json | grep -A 20 '"dependencies"' | head -30

Repository: Mayank-saraswal/moris

Length of output: 831


🏁 Script executed:

cat -n prisma.config.ts 2>/dev/null || echo "File not found at root"

Repository: Mayank-saraswal/moris

Length of output: 473


🏁 Script executed:

rg "prisma" package.json

Repository: Mayank-saraswal/moris

Length of output: 156


🌐 Web query:

Prisma 7 defineConfig import correct path prisma/config

💡 Result:

In Prisma ORM v7, the correct import for defineConfig in prisma.config.ts is:

import "dotenv/config";
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  // ...
});

So the path is "prisma/config" (not @prisma/config). [1], [2]


🌐 Web query:

__dirname ESM JavaScript modules

💡 Result:

In Node.js ES modules (ESM), __dirname isn’t defined (it’s a CommonJS-only variable). Node recommends deriving it from import.meta.url like this: (nodejs.org)

import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

If you’re on Node v20.11.0+ / v21.2.0+, Node also provides import.meta.dirname (and import.meta.filename) directly (file: modules only). (nodejs.org)

Citations:


Wrong package import — "@prisma/config" does not exist; use "prisma/config".

The Prisma 7 official docs import defineConfig and the type-safe env() helper from "prisma/config", which is a subpath export of the prisma package you already have installed. The @prisma/config scoped package does not exist and this import will fail at runtime. Additionally, process.env.DATABASE_URL is typed as string | undefined; using the env() helper provides a type-safe, fail-fast alternative.

__dirname is not available in ESM. The file uses import syntax but __dirname is a CommonJS global. Use import.meta.dirname (Node 20.11.0+) or derive it from import.meta.url.

Proposed fix
-import path from "node:path";
-import dotenv from "dotenv";
-import { defineConfig } from "@prisma/config";
+import "dotenv/config";
+import { defineConfig, env } from "prisma/config";
-dotenv.config({ path: path.resolve(__dirname, ".env.local") });
+// dotenv/config loads .env; remove __dirname and use .env instead of .env.local
 export default defineConfig({
   datasource: {
-    url: process.env.DATABASE_URL,
+    url: env("DATABASE_URL"),
   },
 });

If .env.local override is required for the Prisma CLI specifically, use import { dirname } from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = dirname(fileURLToPath(import.meta.url)); to safely derive __dirname in ESM.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prisma.config.ts` at line 3, The import is incorrect and ESM-only globals are
used: replace the non-existent import from "@prisma/config" with the documented
"prisma/config" and use the type-safe env() helper from that module for
DATABASE_URL (replace direct use of process.env.DATABASE_URL with
env("DATABASE_URL")). Also remove use of CommonJS __dirname; derive a directory
in ESM instead (e.g., compute dirname from import.meta.url or use
import.meta.dirname on Node ≥20.11) if you need to reference files for Prisma;
update any references in this file (e.g., defineConfig, env(), and __dirname
usage) accordingly so the module imports and path resolution work in ESM.

Comment on lines +22 to +31
// Ensure user has a subscription record
let userSub = await prisma.userSubscription.findUnique({
where: { userId },
});

if (!userSub) {
userSub = await prisma.userSubscription.create({
data: { userId, plan: "free" },
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

TOCTOU race condition: concurrent requests can both see null and attempt duplicate inserts.

findUnique + conditional create is not atomic. Two simultaneous subscribe requests for the same user can both pass the null check and both attempt to create, causing a unique constraint violation on userId. Use upsert instead.

Proposed fix
-        // Ensure user has a subscription record
-        let userSub = await prisma.userSubscription.findUnique({
-            where: { userId },
-        });
-
-        if (!userSub) {
-            userSub = await prisma.userSubscription.create({
-                data: { userId, plan: "free" },
-            });
-        }
+        // Ensure user has a subscription record
+        const userSub = await prisma.userSubscription.upsert({
+            where: { userId },
+            update: {},
+            create: { userId, plan: "free" },
+        });
📝 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.

Suggested change
// Ensure user has a subscription record
let userSub = await prisma.userSubscription.findUnique({
where: { userId },
});
if (!userSub) {
userSub = await prisma.userSubscription.create({
data: { userId, plan: "free" },
});
}
// Ensure user has a subscription record
const userSub = await prisma.userSubscription.upsert({
where: { userId },
update: {},
create: { userId, plan: "free" },
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/billing/subscribe/route.ts` around lines 22 - 31, The current
pattern does a non-atomic findUnique followed by create which can race and cause
duplicate-insert unique constraint errors for userSubscription; replace the
findUnique + conditional create logic with a single atomic
prisma.userSubscription.upsert call (use where: { userId }, update: {} and
create: { userId, plan: "free" }) and assign its result back to userSub so
concurrent requests do not conflict.

Comment on lines +9 to +18
const signature = request.headers.get("x-razorpay-signature") ?? "";
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET ?? "";

// Verify signature
if (!verifyWebhookSignature(body, signature, webhookSecret)) {
return NextResponse.json(
{ error: "Invalid signature" },
{ status: 400 }
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Webhook signature verification has two security weaknesses.

  1. Timing-unsafe comparison: verifyWebhookSignature (in src/lib/razorpay.ts:59) uses === instead of crypto.timingSafeEqual, making it susceptible to timing attacks that can incrementally guess the signature.

  2. Empty secret fallback: If RAZORPAY_WEBHOOK_SECRET is unset, webhookSecret defaults to "" (line 10). An attacker can compute HMAC-SHA256("", body) and forge valid webhooks. The handler should fail fast if the secret is missing.

Proposed fix in src/lib/razorpay.ts
 export function verifyWebhookSignature(
     body: string,
     signature: string,
     secret: string
 ): boolean {
-    const crypto = require("crypto");
+    const crypto = await import("node:crypto");
     const expectedSignature = crypto
         .createHmac("sha256", secret)
         .update(body)
         .digest("hex");
-    return expectedSignature === signature;
+    if (expectedSignature.length !== signature.length) return false;
+    return crypto.timingSafeEqual(
+        Buffer.from(expectedSignature, "hex"),
+        Buffer.from(signature, "hex"),
+    );
 }
Proposed fix for empty secret in webhook/route.ts
-    const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET ?? "";
+    const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;
+    if (!webhookSecret) {
+        console.error("RAZORPAY_WEBHOOK_SECRET is not configured");
+        return NextResponse.json({ error: "Server configuration error" }, { status: 500 });
+    }
📝 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.

Suggested change
const signature = request.headers.get("x-razorpay-signature") ?? "";
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET ?? "";
// Verify signature
if (!verifyWebhookSignature(body, signature, webhookSecret)) {
return NextResponse.json(
{ error: "Invalid signature" },
{ status: 400 }
);
}
const signature = request.headers.get("x-razorpay-signature") ?? "";
const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;
if (!webhookSecret) {
console.error("RAZORPAY_WEBHOOK_SECRET is not configured");
return NextResponse.json({ error: "Server configuration error" }, { status: 500 });
}
// Verify signature
if (!verifyWebhookSignature(body, signature, webhookSecret)) {
return NextResponse.json(
{ error: "Invalid signature" },
{ status: 400 }
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/billing/webhook/route.ts` around lines 9 - 18, The webhook
handler currently accepts an empty secret and uses a timing-unsafe equality;
update verifyWebhookSignature to perform HMAC-SHA256 on the payload and compare
the computed and provided signatures using crypto.timingSafeEqual (convert both
to Buffers and first check equal length before timingSafeEqual to avoid
exceptions), and modify the route to fail fast if the secret is missing by
checking process.env.RAZORPAY_WEBHOOK_SECRET (or the local webhookSecret) and
returning an error response (e.g., 500/400) instead of defaulting to "" so no
request is validated against an empty secret.

Comment thread src/app/api/projects/[projectId]/files/route.ts Outdated
Comment on lines +40 to +42
const file = await prisma.file.findUnique({
where: { id: fileId },
select: { id: true, name: true, type: true, projectId: true, blobPath: true },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd "schema.prisma" -x cat {}

Repository: Mayank-saraswal/moris

Length of output: 4234


🏁 Script executed:

cat -n src/features/conversations/inngest/tools/delete-files.ts

Repository: Mayank-saraswal/moris

Length of output: 4182


Critical bug: folder children query uses file.name instead of file.path, and path field is not selected.

Lines 71 and 91 attempt to find folder children using path: { startsWith: ${file.name}/ }, but file.name is only the folder name (e.g., "components"), not the full path (e.g., "src/components"). This query will fail to locate actual children nested under the folder's path.

Additionally, the select clause on line 42 doesn't include the path field, and the filesToDelete type annotation (lines 31-37) omits it, so even if corrected, file.path would be undefined at runtime.

🐛 Required fixes
  1. Add path to the select clause on line 42:
select: { id: true, name: true, type: true, projectId: true, blobPath: true },
+       select: { id: true, name: true, path: true, type: true, projectId: true, blobPath: true },
  1. Add path: string to the filesToDelete type annotation (lines 31-37):
const filesToDelete: {
  id: string;
  name: string;
+ path: string;
  type: string;
  projectId: string;
  blobPath: string | null;
}[] = [];
  1. Replace file.name with file.path on lines 71 and 91:
path: { startsWith: `${file.name}/` },
+       path: { startsWith: `${file.path}/` },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/conversations/inngest/tools/delete-files.ts` around lines 40 -
42, The folder-child lookup bug is caused by selecting only name (not path) and
using file.name for startsWith; update the prisma.file.findUnique select to
include path, add path: string to the filesToDelete type definition, and change
the folder-children queries that currently use file.name to use file.path
instead (e.g., the queries inside the delete-files flow that call path: {
startsWith: `${file.name}/` } should use `${file.path}/` and rely on the
selected file.path).

Comment on lines 56 to 59

// Build tree from path-based files (no content for initial mount)
const fileTree = buildFileTree(files);
await container.mount(fileTree);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's locate the key files mentioned in the review
fd 'buildFileTree|build-file-tree' --type f -e ts -e tsx -e js -e jsx

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Also search for the use-webcontainer file to understand the context
fd 'use-webcontainer' --type f

Repository: Mayank-saraswal/moris

Length of output: 111


🏁 Script executed:

# Search for the useFiles hook
fd 'useFiles|use-files' --type f

Repository: Mayank-saraswal/moris

Length of output: 105


🏁 Script executed:

# Look for API routes related to files
fd 'files.*route|route.*files' --type f

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Search for the Prisma File model definition
fd 'schema.prisma' --type f

Repository: Mayank-saraswal/moris

Length of output: 85


🏁 Script executed:

# Read the use-webcontainer.ts file to see the context and understand the flow
cat -n src/features/preview/hooks/use-webcontainer.ts

Repository: Mayank-saraswal/moris

Length of output: 6408


🏁 Script executed:

# Read the use-files.ts hook
cat -n src/features/projects/hooks/use-files.ts

Repository: Mayank-saraswal/moris

Length of output: 6051


🏁 Script executed:

# Read the Prisma schema to understand File model
cat -n prisma/schema.prisma | head -100

Repository: Mayank-saraswal/moris

Length of output: 3824


🏁 Script executed:

# Find the file-tree utility file
fd 'file-tree' --type f -e ts -e tsx

Repository: Mayank-saraswal/moris

Length of output: 146


🏁 Script executed:

# Search for buildFileTree usage and content loading patterns
rg 'buildFileTree|content.*fetch|blob.*content' --type ts --type tsx -B 3 -A 3

Repository: Mayank-saraswal/moris

Length of output: 92


🏁 Script executed:

# Read the buildFileTree implementation
cat -n src/features/preview/utils/file-tree.tsx

Repository: Mayank-saraswal/moris

Length of output: 2670


🏁 Script executed:

# Search for content fetching or blob handling
rg 'buildFileTree|content|blob' --type ts -B 2 -A 2

Repository: Mayank-saraswal/moris

Length of output: 50377


🏁 Script executed:

# Let me verify the npm install happens immediately after mount by checking the exact flow
sed -n '54,90p' src/features/preview/hooks/use-webcontainer.ts | cat -n

Repository: Mayank-saraswal/moris

Length of output: 1889


🏁 Script executed:

# Check if package.json is typically created with blobPath or inline content
rg 'package\.json' --type ts -B 2 -A 2 | head -50

Repository: Mayank-saraswal/moris

Length of output: 1053


package.json content not available for initial npm install.

The buildFileTree function (line 45) excludes files with blobPath when no inline content is provided: if (content || !file.blobPath). Since buildFileTree(files) at line 58 is called without the optional fileContents parameter, all files resolve to empty content and are filtered out if they have a blobPath. This means files stored in Azure Blob Storage—including package.json—are absent from the initial mount tree.

The npm install command runs immediately at line 72 after the mount completes at line 59, before any content is fetched. This will fail with a missing or empty package.json.

Blob content must be fetched and loaded into the file tree before mounting, or the install step must be deferred until content is available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/preview/hooks/use-webcontainer.ts` around lines 56 - 59, The
mount is missing files that only have blobPath (so package.json is absent)
because buildFileTree(files) is called before blob content is fetched; fetch and
await blob contents for any file with a blobPath (including package.json) and
supply those contents to buildFileTree (or otherwise populate files' content)
before calling container.mount(fileTree), or alternatively defer the npm install
step until after blob fetch and mount complete; reference buildFileTree,
container.mount, files, blobPath and the npm install step to locate where to
preload/await blob content and then proceed with mount and install.

Comment on lines +115 to 128
// Sync file changes to WebContainer (hot-reload)
useEffect(() => {
const container = containerRef.current;
if (!container || !files || status !== "running") return;

const filesMap = new Map(files.map((f) => [f._id, f]));

for (const file of files) {
if (file.type !== "file" || file.storageId || !file.content) continue;

const filePath = getFilePath(file, filesMap);
container.fs.writeFile(filePath, file.content);
for (const file of (files ?? [])) {
if (file.type !== "file") continue;
// Write empty file placeholder — content will be loaded when needed
const filePath = getFilePath(file);
container.fs.writeFile(filePath, "").catch(() => {
// Ignore errors for files that can't be written
});
}
}, [files, status]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and inspect the buildFileTree function
rg -n "buildFileTree" --type=ts -A 30 src/

Repository: Mayank-saraswal/moris

Length of output: 8037


🏁 Script executed:

# Check the FileItem type and useFiles hook
rg -n "FileItem\|useFiles" --type=ts -B 2 -A 5 src/ | head -100

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Examine the use-files.ts to understand the data structure
find src -name "use-files.ts" -o -name "*use-files*" | head -5

Repository: Mayank-saraswal/moris

Length of output: 105


🏁 Script executed:

# Check how files are imported and what properties they have
rg -n "interface FileItem\|type FileItem" --type=ts -A 10

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Get the complete buildFileTree function
cat -n src/features/preview/utils/file-tree.tsx | head -80

Repository: Mayank-saraswal/moris

Length of output: 2670


🏁 Script executed:

# Check the full use-webcontainer.ts to see how buildFileTree is called and what's passed
cat -n src/features/preview/hooks/use-webcontainer.ts

Repository: Mayank-saraswal/moris

Length of output: 6408


🏁 Script executed:

# Inspect use-files.ts to understand the FileRecord type and data structure
cat -n src/features/projects/hooks/use-files.ts

Repository: Mayank-saraswal/moris

Length of output: 6051


🏁 Script executed:

# Double-check if fileContents is ever populated in use-webcontainer.ts
rg -n "fileContents" src/features/preview/hooks/use-webcontainer.ts

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Check if there's any call to fetch file content or pass fileContents to buildFileTree
rg -n "useFileContent\|fetchContent" src/features/preview/ --type=ts

Repository: Mayank-saraswal/moris

Length of output: 47


🏁 Script executed:

# Check if buildFileTree is called anywhere else with fileContents
rg -n "buildFileTree" --type=ts -B 2 -A 5

Repository: Mayank-saraswal/moris

Length of output: 2113


🏁 Script executed:

# Verify the logic: if a file has blobPath and no content, it's excluded from the tree
# Check if any files actually have content loaded during initial mount
rg -n "blobPath\|fileContents" src/features/preview/ --type=ts -B 2 -A 2

Repository: Mayank-saraswal/moris

Length of output: 47


Hot-reload sync writes empty strings to every file, destroying content; files with blob storage are not mounted initially.

The hot-reload effect (lines 115–128) iterates through all files when files changes during the "running" state and writes "" to each path via container.fs.writeFile. This destroys any file content that exists on the mounted filesystem.

Additionally, the initial mount at line 58 calls buildFileTree(files) without populating file content. Since buildFileTree defaults to empty string for missing content (line 44), and the inclusion logic at line 45 excludes files where content || !file.blobPath evaluates to false, files stored in Azure blob storage (with blobPath) are excluded entirely from the mount. This breaks projects that rely on content from blob storage (e.g., package.json required for npm install).

The comment on line 122 claims "content will be loaded when needed," but no mechanism exists to load or sync actual file content—only empty strings are written.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/preview/hooks/use-webcontainer.ts` around lines 115 - 128, The
hot-reload effect is currently overwriting every path with an empty string via
container.fs.writeFile (useEffect referencing containerRef, getFilePath and
container.fs.writeFile) and buildFileTree is excluding files that only have
blobPath, so blob-backed files never get mounted; fix by changing the sync
effect to only write files that have real content (e.g., file.content != null)
and skip writing placeholders for blob-backed files, and update buildFileTree
(and the mounting flow) to include files with blobPath by either pre-fetching
their content before mounting or by wiring a lazy loader that fetches blob
content when the file is first read (ensure getFilePath, buildFileTree, and the
mounting logic treat blobPath files as present rather than omitted).

className="h-auto items-start justify-start p-4 bg-background border rounded-none flex flex-col gap-2"
>
<Link href={`/projects/${data._id}`} className="group">
<Link href={`/projects/${data.id}`} className="group">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check which route pattern exists in the app directory
echo "=== Looking for route files under src/app/projects ==="
fd . src/app/projects -type f 2>/dev/null | head -20

echo "=== Looking for route files under src/app/project ==="
fd . src/app/project -type f 2>/dev/null | head -20

echo "=== All directories at src/app top level ==="
ls -la src/app/ 2>/dev/null | grep '^d'

echo "=== Directory structure of src/app (up to depth 3) ==="
tree -d -L 3 src/app 2>/dev/null || find src/app -type d -maxdepth 3 | sort

Repository: Mayank-saraswal/moris

Length of output: 1111


Fix inconsistent route: ProjectItem links to non-existent /project/ route.

The app defines src/app/projects/[projectId], not /project/. Line 67 must be /projects/${data.id} to match line 41 and the actual route structure, otherwise users will encounter 404 errors when clicking on project items.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/projects/components/projects-list.tsx` at line 41, The
ProjectItem component uses an incorrect route (`/project/${data.id}`) causing
404s; update the Link href in the projects-list component so it points to the
correct route `/projects/${data.id}` to match the app route defined in
src/app/projects/[projectId] (look for the Link usage inside ProjectItem /
projects-list.tsx and replace the singular `/project/` path with `/projects/`).

Comment thread src/lib/e2b-sandbox.ts
@@ -0,0 +1,145 @@
import { Sandbox } from "e2b";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if e2b is declared in any package manifest
fd package.json --max-depth 2 --exec grep -l "e2b" {} 2>/dev/null
echo "---"
# Also check if node_modules/e2b exists or if there's a resolution
rg '"e2b"' --type json -g 'package.json' -n

Repository: Mayank-saraswal/moris

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Show the actual package.json to see what dependencies ARE declared
cat package.json | head -100

Repository: Mayank-saraswal/moris

Length of output: 3728


Add e2b to package.json dependencies.

The file imports Sandbox from "e2b" at line 1, but the package is not declared in the project's package.json. This will cause the import to fail. Add e2b (with the appropriate version) to the dependencies section.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/e2b-sandbox.ts` at line 1, The import of Sandbox from "e2b" in
src/lib/e2b-sandbox.ts will fail because "e2b" is not declared in package.json;
add "e2b" to the dependencies section of package.json (choose the appropriate
semver version for your project or use latest) so the import statement import {
Sandbox } from "e2b"; resolves correctly and runs npm/yarn/pnpm install to
update lockfile.

Comment thread src/lib/redis.ts
Comment on lines +23 to +41
export function createRateLimiter(
requests: number = 20,
windowSeconds: number = 60
) {
return new Ratelimit({
redis: getRedis(),
limiter: Ratelimit.slidingWindow(requests, `${windowSeconds} s`),
analytics: true,
prefix: "moris:ratelimit",
});
}

// Pre-configured rate limiters for different endpoints
export const rateLimiters = {
messages: () => createRateLimiter(15, 60), // 15 messages/min
suggestions: () => createRateLimiter(60, 60), // 60 suggestions/min
quickEdit: () => createRateLimiter(20, 60), // 20 edits/min
billing: () => createRateLimiter(5, 60), // 5 billing actions/min
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

All rate limiters share the same Redis prefix — rate limit counters are conflated across endpoints.

Every limiter created by createRateLimiter uses the fixed prefix "moris:ratelimit". Since Upstash Ratelimit keys are {prefix}:{identifier}, calling rateLimiters.messages().limit(userId) and rateLimiters.suggestions().limit(userId) read/write the same Redis key. The 15 req/min messages limit will incorrectly count suggestion requests (and vice versa), causing premature 429s.

Pass a unique prefix per endpoint:

Proposed fix
 export function createRateLimiter(
+    name: string,
     requests: number = 20,
     windowSeconds: number = 60
 ) {
     return new Ratelimit({
         redis: getRedis(),
         limiter: Ratelimit.slidingWindow(requests, `${windowSeconds} s`),
         analytics: true,
-        prefix: "moris:ratelimit",
+        prefix: `moris:ratelimit:${name}`,
     });
 }
 
 export const rateLimiters = {
-    messages: () => createRateLimiter(15, 60),
-    suggestions: () => createRateLimiter(60, 60),
-    quickEdit: () => createRateLimiter(20, 60),
-    billing: () => createRateLimiter(5, 60),
+    messages: () => createRateLimiter("messages", 15, 60),
+    suggestions: () => createRateLimiter("suggestions", 60, 60),
+    quickEdit: () => createRateLimiter("quick-edit", 20, 60),
+    billing: () => createRateLimiter("billing", 5, 60),
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/redis.ts` around lines 23 - 41, The current createRateLimiter uses a
fixed prefix ("moris:ratelimit") causing counters to be shared across endpoints;
change createRateLimiter to accept a prefix parameter (e.g.,
createRateLimiter(prefix: string, requests?: number, windowSeconds?: number))
and pass that into the Ratelimit config instead of the hard-coded string, then
update the rateLimiters map (messages, suggestions, quickEdit, billing) to call
createRateLimiter with unique prefixes like "moris:ratelimit:messages",
"moris:ratelimit:suggestions", etc., so each endpoint uses its own Redis
keyspace and counters.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
.agents/skills/frontend-design/SKILL.md (1)

42-42: Hardcoded model name Claude reduces portability of the skill.

The closing line names a specific vendor model while the rest of the document is deliberately model-agnostic. If this skill framework is ever used with a different model, or if the product is redistributed, this line becomes misleading.

Consider replacing with a generic reference, e.g.:

✏️ Suggested wording
-Remember: Claude is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
+Remember: this skill is capable of extraordinary creative work. Don't hold back — show what can truly be created when committing fully to a distinctive, well-reasoned vision.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/frontend-design/SKILL.md at line 42, The closing sentence in
.agents/skills/frontend-design/SKILL.md hardcodes the vendor model name
"Claude", reducing portability; update that line to use a model-agnostic phrase
(e.g., "the assistant", "the model", or "the generated creative model") and scan
the file for any other occurrences of "Claude" to replace them similarly so the
skill remains vendor-neutral and consistent with the rest of the document.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/frontend-design/LICENSE.txt:
- Line 177: The LICENSE.txt is missing the canonical Apache License 2.0 APPENDIX
after the line "END OF TERMS AND CONDITIONS"; append the standard informational
APPENDIX text from the official Apache License 2.0 template (the paragraph(s)
beginning with "APPENDIX: How to apply the Apache License to your work"
including the short guidance on including a NOTICE file and recommended header)
immediately following "END OF TERMS AND CONDITIONS" so the file matches the
canonical template.

In @.agents/skills/ui-ux-pro-max/SKILL.md:
- Line 3: Standardize the capability counts by choosing a canonical set (e.g.,
the values currently in the description string: "50 styles, 21 palettes, 50 font
pairings, 20 charts, 9 stacks") and update every duplicated occurrence in
SKILL.md so they match exactly; search for the "description:" field and all
other places that list palettes/font pairings/charts/stacks and replace their
counts to the chosen canonical numbers, keeping formatting and punctuation
identical to the original description string.
- Around line 178-184: The fenced code block in SKILL.md is missing a language
identifier which triggers markdownlint MD040; update the opening fence ("```")
to include a language (e.g., "```text" or "```md") so the block becomes
"```text" and keep the block contents unchanged; search for the exact fenced
block text in SKILL.md to locate and update it.
- Around line 138-139: The CLI examples in SKILL.md use the non-existent command
string "python3 skills/ui-ux-pro-max/scripts/search.py ..." so update all
affected examples to point to a valid executable path or add the missing script
and fix the symlink: either 1) change every occurrence of "python3
skills/ui-ux-pro-max/scripts/search.py" to the real runnable path (e.g., the
actual script location under .agents/skills/ui-ux-pro-max/scripts or the correct
src path used in the repo), or 2) implement a search.py at the expected location
(and update/repair the symlink target to ../../../src/ui-ux-pro-max/scripts if
you place it under src) so the command works as documented; ensure each example
(the ones using the "<product_type> <industry> <keywords>" pattern and flags
like --design-system / -p) is updated consistently.

In @.agents/skills/web-design-guidelines/SKILL.md:
- Around line 25-27: The fenced code block containing the raw URL (the block
with
"https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md")
lacks a language identifier and triggers markdownlint MD040; update that fenced
block in SKILL.md by adding a language tag (e.g., use "text" or "markdown" such
as ```text) immediately after the opening backticks so the block becomes fenced
with a language identifier.
- Around line 16-27: Replace the mutable guidelines URL under the "Guidelines
Source" block with a commit-pinned URL (replace `.../main/command.md` with the
same path at a specific commit SHA) so reviews are deterministic; update the
text near the URL to note the pinned commit SHA and add a short bumping policy
(e.g., "bump monthly or on-demand via manual review" and how to update the
`<commit-sha>`) so maintainers know when/how to advance the pinned revision —
edit the SKILL.md Guidelines Source section to perform these changes.

---

Nitpick comments:
In @.agents/skills/frontend-design/SKILL.md:
- Line 42: The closing sentence in .agents/skills/frontend-design/SKILL.md
hardcodes the vendor model name "Claude", reducing portability; update that line
to use a model-agnostic phrase (e.g., "the assistant", "the model", or "the
generated creative model") and scan the file for any other occurrences of
"Claude" to replace them similarly so the skill remains vendor-neutral and
consistent with the rest of the document.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59d2559 and 2ff3498.

📒 Files selected for processing (8)
  • .agents/skills/frontend-design/LICENSE.txt
  • .agents/skills/frontend-design/SKILL.md
  • .agents/skills/ui-ux-pro-max/SKILL.md
  • .agents/skills/ui-ux-pro-max/data
  • .agents/skills/ui-ux-pro-max/scripts
  • .agents/skills/web-design-guidelines/SKILL.md
  • skills-lock.json
  • src/app/api/projects/[projectId]/files/route.ts
✅ Files skipped from review due to trivial changes (3)
  • skills-lock.json
  • .agents/skills/ui-ux-pro-max/data
  • .agents/skills/ui-ux-pro-max/scripts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/api/projects/[projectId]/files/route.ts

incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the LICENSE.txt file
cat -n .agents/skills/frontend-design/LICENSE.txt | tail -20

Repository: Mayank-saraswal/moris

Length of output: 1426


🌐 Web query:

Apache License 2.0 full canonical text complete official template appendix

💡 Result:

Canonical official Apache License 2.0 (full text + official APPENDIX template):

  • Plain-text canonical license (includes “END OF TERMS AND CONDITIONS” + “APPENDIX: How to apply the Apache License to your work”): (apache.org)
https://www.apache.org/licenses/LICENSE-2.0.txt
  • HTML canonical license page (same content; links to the plain-text “Text version”): (apache.org)
https://www.apache.org/licenses/LICENSE-2.0

Citations:


License file is missing the standard Apache 2.0 Appendix.

The canonical Apache License 2.0 template includes an informational APPENDIX after END OF TERMS AND CONDITIONS explaining how to apply the license to a work. While its omission doesn't invalidate the license, using the full canonical text avoids ambiguity and is conventional practice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/frontend-design/LICENSE.txt at line 177, The LICENSE.txt is
missing the canonical Apache License 2.0 APPENDIX after the line "END OF TERMS
AND CONDITIONS"; append the standard informational APPENDIX text from the
official Apache License 2.0 template (the paragraph(s) beginning with "APPENDIX:
How to apply the Apache License to your work" including the short guidance on
including a NOTICE file and recommended header) immediately following "END OF
TERMS AND CONDITIONS" so the file matches the canonical template.

@@ -0,0 +1,386 @@
---
name: ui-ux-pro-max
description: "UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient. Integrations: shadcn/ui MCP for component search and examples."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Resolve conflicting capability counts across the document.

Line 3 says 21 palettes / 50 font pairings / 20 charts / 9 stacks, while Line 8 and Line 212 present different totals. Please keep one canonical set of numbers to avoid user confusion.

Also applies to: 8-8, 212-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/ui-ux-pro-max/SKILL.md at line 3, Standardize the capability
counts by choosing a canonical set (e.g., the values currently in the
description string: "50 styles, 21 palettes, 50 font pairings, 20 charts, 9
stacks") and update every duplicated occurrence in SKILL.md so they match
exactly; search for the "description:" field and all other places that list
palettes/font pairings/charts/stacks and replace their counts to the chosen
canonical numbers, keeping formatting and punctuation identical to the original
description string.

Comment on lines +138 to +139
python3 skills/ui-ux-pro-max/scripts/search.py "<product_type> <industry> <keywords>" --design-system [-p "Project Name"]
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Finding all search.py files ==="
fd -a 'search.py$'

echo ""
echo "=== Checking if skills directory exists at root ==="
if [ -d "skills" ]; then
  ls -la skills/
else
  echo "skills/ directory does NOT exist at repository root"
fi

echo ""
echo "=== Checking .agents/skills structure ==="
if [ -d ".agents/skills" ]; then
  ls -la .agents/skills/
fi

echo ""
echo "=== Checking for symlinks ==="
ls -la | grep "skills\|agents"

echo ""
echo "=== Reading SKILL.md lines mentioned in review ==="
wc -l .agents/skills/ui-ux-pro-max/SKILL.md

Repository: Mayank-saraswal/moris

Length of output: 805


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Extract command examples from lines 138-139 ==="
sed -n '138,139p' .agents/skills/ui-ux-pro-max/SKILL.md

echo ""
echo "=== Extract command examples from all mentioned lines ==="
echo "Lines 149-150:"
sed -n '149,150p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 157-158:"
sed -n '157,158p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 166-167:"
sed -n '166,167p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 191-192:"
sed -n '191,192p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 209-210:"
sed -n '209,210p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 263-264:"
sed -n '263,264p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 272-276:"
sed -n '272,276p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 281-282:"
sed -n '281,282p' .agents/skills/ui-ux-pro-max/SKILL.md
echo ""
echo "Lines 294-298:"
sed -n '294,298p' .agents/skills/ui-ux-pro-max/SKILL.md

Repository: Mayank-saraswal/moris

Length of output: 1636


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Contents of .agents/skills/ui-ux-pro-max/ ==="
ls -la .agents/skills/ui-ux-pro-max/

echo ""
echo "=== Check if scripts directory exists ==="
if [ -d ".agents/skills/ui-ux-pro-max/scripts" ]; then
  echo "scripts/ directory exists. Contents:"
  ls -la .agents/skills/ui-ux-pro-max/scripts/
else
  echo "scripts/ directory does NOT exist at .agents/skills/ui-ux-pro-max/"
fi

Repository: Mayank-saraswal/moris

Length of output: 510


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check what 'scripts' actually is ==="
file .agents/skills/ui-ux-pro-max/scripts
cat .agents/skills/ui-ux-pro-max/scripts

echo ""
echo "=== Verify the exact relative path from SKILL.md ==="
cd .agents/skills/ui-ux-pro-max
echo "Current location: $(pwd)"
echo "SKILL.md is in this directory"
echo "If scripts/search.py existed here, the relative path would be: ./scripts/search.py or scripts/search.py"
echo "But from repository root, it would be: .agents/skills/ui-ux-pro-max/scripts/search.py"

Repository: Mayank-saraswal/moris

Length of output: 167


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check what 'scripts' actually is ==="
cat .agents/skills/ui-ux-pro-max/scripts
echo ""

echo "=== Check what 'data' actually is ==="
cat .agents/skills/ui-ux-pro-max/data
echo ""

echo "=== List entire repository structure to understand layout ==="
find . -maxdepth 3 -type f -name "*.py" | head -20

Repository: Mayank-saraswal/moris

Length of output: 272


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Resolve the symlink pointers ==="
echo "scripts pointer content: $(cat .agents/skills/ui-ux-pro-max/scripts)"
echo "Resolving to: src/ui-ux-pro-max/scripts"

echo ""
echo "=== Check if src/ui-ux-pro-max/scripts/search.py exists ==="
if [ -f "src/ui-ux-pro-max/scripts/search.py" ]; then
  echo "✓ Found: src/ui-ux-pro-max/scripts/search.py"
  wc -l src/ui-ux-pro-max/scripts/search.py
else
  echo "✗ NOT found at src/ui-ux-pro-max/scripts/search.py"
fi

echo ""
echo "=== Find all search.py files in the repository ==="
find . -name "search.py" -type f 2>/dev/null || echo "No search.py found"

echo ""
echo "=== List repository root structure ==="
ls -la | grep -E "^d|^l"

Repository: Mayank-saraswal/moris

Length of output: 798


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Explore src/ui-ux-pro-max directory ==="
if [ -d "src/ui-ux-pro-max" ]; then
  echo "src/ui-ux-pro-max exists. Contents:"
  ls -la src/ui-ux-pro-max/
else
  echo "src/ui-ux-pro-max does NOT exist"
fi

echo ""
echo "=== List all files in src ==="
find src -maxdepth 3 -type f | head -30

Repository: Mayank-saraswal/moris

Length of output: 1294


Update CLI path examples: commands reference non-existent paths and script files.

The command examples throughout this file use python3 skills/ui-ux-pro-max/scripts/search.py, but:

  1. No skills/ directory exists at the repository root
  2. The symlink at .agents/skills/ui-ux-pro-max/scripts points to ../../../src/ui-ux-pro-max/scripts, which does not exist
  3. The search.py script is not present in the repository

These documented commands will fail when copied and executed. Update all command examples to use the correct path, or implement the missing script file(s).

Affected lines: 138-139, 149-150, 157-158, 166-167, 191-192, 209-210, 263-264, 272-276, 281-282, 294-298

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/ui-ux-pro-max/SKILL.md around lines 138 - 139, The CLI
examples in SKILL.md use the non-existent command string "python3
skills/ui-ux-pro-max/scripts/search.py ..." so update all affected examples to
point to a valid executable path or add the missing script and fix the symlink:
either 1) change every occurrence of "python3
skills/ui-ux-pro-max/scripts/search.py" to the real runnable path (e.g., the
actual script location under .agents/skills/ui-ux-pro-max/scripts or the correct
src path used in the repo), or 2) implement a search.py at the expected location
(and update/repair the symlink target to ../../../src/ui-ux-pro-max/scripts if
you place it under src) so the command works as documented; ensure each example
(the ones using the "<product_type> <industry> <keywords>" pattern and flags
like --design-system / -p) is updated consistently.

Comment on lines +178 to +184
```
I am building the [Page Name] page. Please read design-system/MASTER.md.
Also check if design-system/pages/[page-name].md exists.
If the page file exists, prioritize its rules.
If not, use the Master rules exclusively.
Now, generate the code...
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language identifier to the fenced code block.

Line 178 starts a fenced block without a language, which triggers markdownlint MD040.

Suggested fix
-```
+```text
 I am building the [Page Name] page. Please read design-system/MASTER.md.
 Also check if design-system/pages/[page-name].md exists.
 If the page file exists, prioritize its rules.
 If not, use the Master rules exclusively.
 Now, generate the code...
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

[warning] 178-178: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.agents/skills/ui-ux-pro-max/SKILL.md around lines 178 - 184, The fenced
code block in SKILL.md is missing a language identifier which triggers
markdownlint MD040; update the opening fence ("") to include a language (e.g., "text" or "md") so the block becomes "text" and keep the block
contents unchanged; search for the exact fenced block text in SKILL.md to locate
and update it.


</details>

<!-- fingerprinting:phantom:triton:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +16 to +27
1. Fetch the latest guidelines from the source URL below
2. Read the specified files (or prompt user for files/pattern)
3. Check against all rules in the fetched guidelines
4. Output findings in the terse `file:line` format

## Guidelines Source

Fetch fresh guidelines before each review:

```
https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the file exists and show its full content
if [ -f ".agents/skills/web-design-guidelines/SKILL.md" ]; then
  echo "=== File exists. Full content with line numbers ==="
  cat -n ".agents/skills/web-design-guidelines/SKILL.md"
else
  echo "File not found. Searching for SKILL.md files..."
  find . -name "SKILL.md" -type f 2>/dev/null | head -20
fi

Repository: Mayank-saraswal/moris

Length of output: 1622


🏁 Script executed:

#!/bin/bash
# Search for raw GitHub URLs with mutable branches (main/master)
echo "=== Searching for mutable raw GitHub URLs in .md files ==="
rg -n --type md 'raw\.githubusercontent\.com/.+/(main|master)/' --max-count=20

Repository: Mayank-saraswal/moris

Length of output: 123


Pin the guidelines source to an immutable revision.

The URL at line 26 currently points to the mutable main branch, making review outcomes nondeterministic and allowing upstream rule changes to alter behavior unexpectedly.

Proposed fix
-https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
+https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/<commit-sha>/command.md

Add a short note on how/when to bump <commit-sha> (e.g., monthly or manual review).

📝 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.

Suggested change
1. Fetch the latest guidelines from the source URL below
2. Read the specified files (or prompt user for files/pattern)
3. Check against all rules in the fetched guidelines
4. Output findings in the terse `file:line` format
## Guidelines Source
Fetch fresh guidelines before each review:
```
https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
```
1. Fetch the latest guidelines from the source URL below
2. Read the specified files (or prompt user for files/pattern)
3. Check against all rules in the fetched guidelines
4. Output findings in the terse `file:line` format
## Guidelines Source
Fetch fresh guidelines before each review:
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/web-design-guidelines/SKILL.md around lines 16 - 27, Replace
the mutable guidelines URL under the "Guidelines Source" block with a
commit-pinned URL (replace `.../main/command.md` with the same path at a
specific commit SHA) so reviews are deterministic; update the text near the URL
to note the pinned commit SHA and add a short bumping policy (e.g., "bump
monthly or on-demand via manual review" and how to update the `<commit-sha>`) so
maintainers know when/how to advance the pinned revision — edit the SKILL.md
Guidelines Source section to perform these changes.

Comment on lines +25 to +27
```
https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language identifier to the fenced block.

Line 25 starts a fenced code block without a language, which triggers markdownlint MD040.

Suggested fix
-```
+```text
 https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/web-design-guidelines/SKILL.md around lines 25 - 27, The
fenced code block containing the raw URL (the block with
"https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/command.md")
lacks a language identifier and triggers markdownlint MD040; update that fenced
block in SKILL.md by adding a language tag (e.g., use "text" or "markdown" such
as ```text) immediately after the opening backticks so the block becomes fenced
with a language identifier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant