From 27248e97c615a0999ab3407c50cc73e076493ae3 Mon Sep 17 00:00:00 2001 From: Soumik15630m Date: Sat, 21 Mar 2026 20:49:21 +0530 Subject: [PATCH 01/27] feat: Add code-review-agent AgentKit --- kits/agentic/code-review/.env.example | 4 + kits/agentic/code-review/.gitignore | 27 + .../code-review/.next-dev-3001.err.log | 0 kits/agentic/code-review/.next-dev-3001.log | 16 + kits/agentic/code-review/README.md | 106 ++ .../code-review/actions/orchestrate.ts | 84 + .../code-review/app/api/review/route.ts | 139 ++ kits/agentic/code-review/app/globals.css | 11 + kits/agentic/code-review/app/layout.tsx | 19 + kits/agentic/code-review/app/page.tsx | 729 ++++++++ kits/agentic/code-review/components.json | 21 + .../agentic/code-review/components/header.tsx | 47 + .../code-review/components/theme-provider.tsx | 11 + .../code-review/components/ui/accordion.tsx | 66 + .../components/ui/alert-dialog.tsx | 157 ++ .../code-review/components/ui/alert.tsx | 66 + .../components/ui/aspect-ratio.tsx | 11 + .../code-review/components/ui/avatar.tsx | 53 + .../code-review/components/ui/badge.tsx | 46 + .../code-review/components/ui/breadcrumb.tsx | 109 ++ .../components/ui/button-group.tsx | 83 + .../code-review/components/ui/button.tsx | 60 + .../code-review/components/ui/calendar.tsx | 213 +++ .../code-review/components/ui/card.tsx | 92 + .../code-review/components/ui/carousel.tsx | 241 +++ .../code-review/components/ui/chart.tsx | 353 ++++ .../code-review/components/ui/checkbox.tsx | 32 + .../code-review/components/ui/collapsible.tsx | 33 + .../code-review/components/ui/command.tsx | 184 ++ .../components/ui/context-menu.tsx | 252 +++ .../code-review/components/ui/dialog.tsx | 143 ++ .../code-review/components/ui/drawer.tsx | 135 ++ .../components/ui/dropdown-menu.tsx | 257 +++ .../code-review/components/ui/empty.tsx | 104 ++ .../code-review/components/ui/field.tsx | 244 +++ .../code-review/components/ui/form.tsx | 167 ++ .../code-review/components/ui/hover-card.tsx | 44 + .../code-review/components/ui/input-group.tsx | 169 ++ .../code-review/components/ui/input-otp.tsx | 77 + .../code-review/components/ui/input.tsx | 21 + .../code-review/components/ui/item.tsx | 193 ++ .../agentic/code-review/components/ui/kbd.tsx | 28 + .../code-review/components/ui/label.tsx | 24 + .../code-review/components/ui/menubar.tsx | 276 +++ .../components/ui/navigation-menu.tsx | 166 ++ .../code-review/components/ui/pagination.tsx | 127 ++ .../code-review/components/ui/popover.tsx | 48 + .../code-review/components/ui/progress.tsx | 31 + .../code-review/components/ui/radio-group.tsx | 45 + .../code-review/components/ui/resizable.tsx | 56 + .../code-review/components/ui/scroll-area.tsx | 58 + .../code-review/components/ui/select.tsx | 185 ++ .../code-review/components/ui/separator.tsx | 28 + .../code-review/components/ui/sheet.tsx | 139 ++ .../code-review/components/ui/sidebar.tsx | 726 ++++++++ .../code-review/components/ui/skeleton.tsx | 13 + .../code-review/components/ui/slider.tsx | 63 + .../code-review/components/ui/sonner.tsx | 25 + .../code-review/components/ui/spinner.tsx | 16 + .../code-review/components/ui/switch.tsx | 31 + .../code-review/components/ui/table.tsx | 116 ++ .../code-review/components/ui/tabs.tsx | 66 + .../code-review/components/ui/textarea.tsx | 18 + .../code-review/components/ui/toast.tsx | 129 ++ .../code-review/components/ui/toaster.tsx | 35 + .../components/ui/toggle-group.tsx | 73 + .../code-review/components/ui/toggle.tsx | 47 + .../code-review/components/ui/tooltip.tsx | 61 + .../code-review/components/ui/use-mobile.tsx | 19 + .../code-review/components/ui/use-toast.ts | 191 ++ kits/agentic/code-review/config.json | 22 + .../flows/agentic-generate-content/README.md | 65 + .../agentic-generate-content/config.json | 386 ++++ .../agentic-generate-content/inputs.json | 66 + .../flows/agentic-generate-content/meta.json | 12 + kits/agentic/code-review/hooks/use-mobile.ts | 19 + kits/agentic/code-review/hooks/use-toast.ts | 191 ++ .../agentic/code-review/lib/lamatic-client.ts | 20 + kits/agentic/code-review/lib/utils.ts | 6 + kits/agentic/code-review/next.config.mjs | 11 + kits/agentic/code-review/package-lock.json | 1594 +++++++++++++++++ kits/agentic/code-review/package.json | 25 + kits/agentic/code-review/tsconfig.json | 35 + 83 files changed, 10111 insertions(+) create mode 100644 kits/agentic/code-review/.env.example create mode 100644 kits/agentic/code-review/.gitignore create mode 100644 kits/agentic/code-review/.next-dev-3001.err.log create mode 100644 kits/agentic/code-review/.next-dev-3001.log create mode 100644 kits/agentic/code-review/README.md create mode 100644 kits/agentic/code-review/actions/orchestrate.ts create mode 100644 kits/agentic/code-review/app/api/review/route.ts create mode 100644 kits/agentic/code-review/app/globals.css create mode 100644 kits/agentic/code-review/app/layout.tsx create mode 100644 kits/agentic/code-review/app/page.tsx create mode 100644 kits/agentic/code-review/components.json create mode 100644 kits/agentic/code-review/components/header.tsx create mode 100644 kits/agentic/code-review/components/theme-provider.tsx create mode 100644 kits/agentic/code-review/components/ui/accordion.tsx create mode 100644 kits/agentic/code-review/components/ui/alert-dialog.tsx create mode 100644 kits/agentic/code-review/components/ui/alert.tsx create mode 100644 kits/agentic/code-review/components/ui/aspect-ratio.tsx create mode 100644 kits/agentic/code-review/components/ui/avatar.tsx create mode 100644 kits/agentic/code-review/components/ui/badge.tsx create mode 100644 kits/agentic/code-review/components/ui/breadcrumb.tsx create mode 100644 kits/agentic/code-review/components/ui/button-group.tsx create mode 100644 kits/agentic/code-review/components/ui/button.tsx create mode 100644 kits/agentic/code-review/components/ui/calendar.tsx create mode 100644 kits/agentic/code-review/components/ui/card.tsx create mode 100644 kits/agentic/code-review/components/ui/carousel.tsx create mode 100644 kits/agentic/code-review/components/ui/chart.tsx create mode 100644 kits/agentic/code-review/components/ui/checkbox.tsx create mode 100644 kits/agentic/code-review/components/ui/collapsible.tsx create mode 100644 kits/agentic/code-review/components/ui/command.tsx create mode 100644 kits/agentic/code-review/components/ui/context-menu.tsx create mode 100644 kits/agentic/code-review/components/ui/dialog.tsx create mode 100644 kits/agentic/code-review/components/ui/drawer.tsx create mode 100644 kits/agentic/code-review/components/ui/dropdown-menu.tsx create mode 100644 kits/agentic/code-review/components/ui/empty.tsx create mode 100644 kits/agentic/code-review/components/ui/field.tsx create mode 100644 kits/agentic/code-review/components/ui/form.tsx create mode 100644 kits/agentic/code-review/components/ui/hover-card.tsx create mode 100644 kits/agentic/code-review/components/ui/input-group.tsx create mode 100644 kits/agentic/code-review/components/ui/input-otp.tsx create mode 100644 kits/agentic/code-review/components/ui/input.tsx create mode 100644 kits/agentic/code-review/components/ui/item.tsx create mode 100644 kits/agentic/code-review/components/ui/kbd.tsx create mode 100644 kits/agentic/code-review/components/ui/label.tsx create mode 100644 kits/agentic/code-review/components/ui/menubar.tsx create mode 100644 kits/agentic/code-review/components/ui/navigation-menu.tsx create mode 100644 kits/agentic/code-review/components/ui/pagination.tsx create mode 100644 kits/agentic/code-review/components/ui/popover.tsx create mode 100644 kits/agentic/code-review/components/ui/progress.tsx create mode 100644 kits/agentic/code-review/components/ui/radio-group.tsx create mode 100644 kits/agentic/code-review/components/ui/resizable.tsx create mode 100644 kits/agentic/code-review/components/ui/scroll-area.tsx create mode 100644 kits/agentic/code-review/components/ui/select.tsx create mode 100644 kits/agentic/code-review/components/ui/separator.tsx create mode 100644 kits/agentic/code-review/components/ui/sheet.tsx create mode 100644 kits/agentic/code-review/components/ui/sidebar.tsx create mode 100644 kits/agentic/code-review/components/ui/skeleton.tsx create mode 100644 kits/agentic/code-review/components/ui/slider.tsx create mode 100644 kits/agentic/code-review/components/ui/sonner.tsx create mode 100644 kits/agentic/code-review/components/ui/spinner.tsx create mode 100644 kits/agentic/code-review/components/ui/switch.tsx create mode 100644 kits/agentic/code-review/components/ui/table.tsx create mode 100644 kits/agentic/code-review/components/ui/tabs.tsx create mode 100644 kits/agentic/code-review/components/ui/textarea.tsx create mode 100644 kits/agentic/code-review/components/ui/toast.tsx create mode 100644 kits/agentic/code-review/components/ui/toaster.tsx create mode 100644 kits/agentic/code-review/components/ui/toggle-group.tsx create mode 100644 kits/agentic/code-review/components/ui/toggle.tsx create mode 100644 kits/agentic/code-review/components/ui/tooltip.tsx create mode 100644 kits/agentic/code-review/components/ui/use-mobile.tsx create mode 100644 kits/agentic/code-review/components/ui/use-toast.ts create mode 100644 kits/agentic/code-review/config.json create mode 100644 kits/agentic/code-review/flows/agentic-generate-content/README.md create mode 100644 kits/agentic/code-review/flows/agentic-generate-content/config.json create mode 100644 kits/agentic/code-review/flows/agentic-generate-content/inputs.json create mode 100644 kits/agentic/code-review/flows/agentic-generate-content/meta.json create mode 100644 kits/agentic/code-review/hooks/use-mobile.ts create mode 100644 kits/agentic/code-review/hooks/use-toast.ts create mode 100644 kits/agentic/code-review/lib/lamatic-client.ts create mode 100644 kits/agentic/code-review/lib/utils.ts create mode 100644 kits/agentic/code-review/next.config.mjs create mode 100644 kits/agentic/code-review/package-lock.json create mode 100644 kits/agentic/code-review/package.json create mode 100644 kits/agentic/code-review/tsconfig.json diff --git a/kits/agentic/code-review/.env.example b/kits/agentic/code-review/.env.example new file mode 100644 index 00000000..18cea69d --- /dev/null +++ b/kits/agentic/code-review/.env.example @@ -0,0 +1,4 @@ +AGENTIC_GENERATE_CONTENT = "AGENTIC_GENERATE_CONTENT Flow ID" +LAMATIC_API_URL = "LAMATIC_API_URL" +LAMATIC_PROJECT_ID = "LAMATIC_PROJECT_ID" +LAMATIC_API_KEY = "LAMATIC_API_KEY" \ No newline at end of file diff --git a/kits/agentic/code-review/.gitignore b/kits/agentic/code-review/.gitignore new file mode 100644 index 00000000..b28ea810 --- /dev/null +++ b/kits/agentic/code-review/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/kits/agentic/code-review/.next-dev-3001.err.log b/kits/agentic/code-review/.next-dev-3001.err.log new file mode 100644 index 00000000..e69de29b diff --git a/kits/agentic/code-review/.next-dev-3001.log b/kits/agentic/code-review/.next-dev-3001.log new file mode 100644 index 00000000..7e00146d --- /dev/null +++ b/kits/agentic/code-review/.next-dev-3001.log @@ -0,0 +1,16 @@ + +> code-review-agent@0.1.0 dev +> next dev --port 3001 + + ▲ Next.js 15.1.3 + - Local: http://localhost:3001 + - Network: http://10.72.173.67:3001 + - Environments: .env + + ✓ Starting... + ✓ Ready in 3.5s + ○ Compiling / ... + ✓ Compiled / in 1390ms (571 modules) + GET / 200 in 1935ms + GET / 200 in 36ms +[?25h diff --git a/kits/agentic/code-review/README.md b/kits/agentic/code-review/README.md new file mode 100644 index 00000000..b88ab96f --- /dev/null +++ b/kits/agentic/code-review/README.md @@ -0,0 +1,106 @@ +# Agent Kit Generation by Lamatic.ai + +

+ + Live Demo + +

+ + +**Agent Kit Generation** is an AI-powered content generation system built with [Lamatic.ai](https://lamatic.ai). It uses intelligent workflows to generate text, images, and JSON content through a modern Next.js interface with markdown rendering support. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits/agentic/generation&env=AGENTIC_GENERATE_CONTENT,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY&envDescription=Your%20Lamatic%20Generation%20keys%20are%20required.&envLink=https://lamatic.ai/templates/agentkits/agentic/agent-kit-generation) + +--- + +## Lamatic Setup (Pre and Post) + +Before running this project, you must build and deploy the flow in Lamatic, then wire its config into this codebase. + +Pre: Build in Lamatic +1. Sign in or sign up at https://lamatic.ai +2. Create a project (if you don’t have one yet) +3. Click “+ New Flow” and select "Templates" +4. Select the 'Generation' agent kit +5. Configure providers/tools/inputs as prompted +6. Deploy the kit in Lamatic and obtain your .env keys +7. Copy the keys from your studio + +Post: Wire into this repo +1. Create a .env file and set the keys +2. Install and run locally: + - npm install + - npm run dev +3. Deploy (Vercel recommended): + - Import your repo, set the project's Root Directory (if applicable) + - Add env vars in Vercel (same as your .env) + - Deploy and test your live URL + +Notes +- Coming soon: single-click export and "Connect Git" in Lamatic to push config directly to your repo. + +--- + +## 🔑 Setup +## Required Keys and Config + +You’ll need these things to run this project locally: + +1. **.env Keys** → get it from your [Lamatic account](https://lamatic.ai) post kit deployment. + + +| Item | Purpose | Where to Get It | +| ----------------- | -------------------------------------------- | ----------------------------------------------- | +| .env Key | Authentication for Lamatic AI APIs and Orchestration | [lamatic.ai](https://lamatic.ai) | + +### 1. Environment Variables + +Create `.env.local` with: + +```bash +# Lamatic +AGENTIC_GENERATE_CONTENT = "AGENTIC_GENERATE_CONTENT Flow ID" +LAMATIC_API_URL = "LAMATIC_API_URL" +LAMATIC_PROJECT_ID = "LAMATIC_PROJECT_ID" +LAMATIC_API_KEY = "LAMATIC_API_KEY" +``` + +### 2. Install & Run + +```bash +npm install +npm run dev +# Open http://localhost:3000 +``` +--- + +## 📂 Repo Structure + +``` +/actions + └── orchestrate.ts # Lamatic workflow orchestration +/app + └── page.tsx # Main generation form UI +/components + ├── header.tsx # Header component with navigation + └── ui # shadcn/ui components +/lib + └── lamatic-client.ts # Lamatic SDK client +/public + └── lamatic-logo.png # Lamatic branding +/flows + └── ... # Lamatic Flows +/package.json # Dependencies & scripts +``` + +--- + +## 🤝 Contributing + +We welcome contributions! Open an issue or PR in this repo. + +--- + +## 📜 License + +MIT License – see [LICENSE](./LICENSE). diff --git a/kits/agentic/code-review/actions/orchestrate.ts b/kits/agentic/code-review/actions/orchestrate.ts new file mode 100644 index 00000000..78bd9438 --- /dev/null +++ b/kits/agentic/code-review/actions/orchestrate.ts @@ -0,0 +1,84 @@ +"use server" + +import { lamaticClient } from "@/lib/lamatic-client" +import {config} from "../orchestrate.js" + +type InputType = "text" | "image" | "json" + +export async function generateContent( + inputType: InputType, + instructions: string, +): Promise<{ + success: boolean + data?: any + error?: string +}> { + try { + console.log("[v0] Generating content with:", { inputType, instructions }) + + // Get the first workflow from the config + const flows = config.flows + const firstFlowKey = Object.keys(flows)[0] + + if (!firstFlowKey) { + throw new Error("No workflows found in configuration") + } + + // Fix: Add index signature to make TypeScript happy about accessing flows[firstFlowKey] + const flow = flows[firstFlowKey as keyof typeof flows] as (typeof flows)[keyof typeof flows]; + console.log("[v0] Using workflow:", flow.name, flow.workflowId); + + // Prepare inputs based on the flow's input schema + const inputs: Record = { + mode: inputType, + instructions, + } + + // Map to schema if needed + for (const inputKey of Object.keys(flow.inputSchema || {})) { + if (inputKey === "inputType" || inputKey === "type") { + inputs[inputKey] = inputType + } else if (inputKey === "instructions" || inputKey === "query") { + inputs[inputKey] = instructions + } + } + + console.log("[v0] Sending inputs:", inputs) + + if(!flow.workflowId){ + throw Error("Workflow not found in config.") + } + const resData = await lamaticClient.executeFlow(flow.workflowId, inputs) + console.log("[v0] Raw response:", resData) + + // Parse the answer from resData?.output.answer + const answer = resData?.result?.answer + + if (!answer) { + throw new Error("No answer found in response") + } + + return { + success: true, + data: answer, + } + } catch (error) { + console.error("[v0] Generation error:", error) + + let errorMessage = "Unknown error occurred" + if (error instanceof Error) { + errorMessage = error.message + if (error.message.includes("fetch failed")) { + errorMessage = + "Network error: Unable to connect to the service. Please check your internet connection and try again." + } else if (error.message.includes("API key")) { + errorMessage = "Authentication error: Please check your API configuration." + } + } + + return { + success: false, + error: errorMessage, + } + } +} diff --git a/kits/agentic/code-review/app/api/review/route.ts b/kits/agentic/code-review/app/api/review/route.ts new file mode 100644 index 00000000..29e108df --- /dev/null +++ b/kits/agentic/code-review/app/api/review/route.ts @@ -0,0 +1,139 @@ +import { NextRequest, NextResponse } from "next/server" + +const query = ` + query ExecuteWorkflow( + $workflowId: String! + $owner: String + $repo: String + $pr_number: String + ) { + executeWorkflow( + workflowId: $workflowId + payload: { + owner: $owner + repo: $repo + pr_number: $pr_number + } + ) { + status + result + } + } +` + +export async function POST(req: NextRequest) { + try { + const body = await req.json() + const owner = typeof body?.owner === "string" ? body.owner.trim() : "" + const repo = typeof body?.repo === "string" ? body.repo.trim() : "" + const pr_number = + typeof body?.pr_number === "string" ? body.pr_number.trim() : "" + + if (!owner || !repo || !pr_number) { + return NextResponse.json( + { error: "Owner, repo, and PR number are required." }, + { status: 400 } + ) + } + + if (!process.env.LAMATIC_API_KEY) { + return NextResponse.json( + { error: "LAMATIC_API_KEY is not configured on the server." }, + { status: 500 } + ) + } + + const res = await fetch( + "https://soumiksorganization573-codereviewagent135.lamatic.dev/graphql", + { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.LAMATIC_API_KEY}`, + "Content-Type": "application/json", + "x-project-id": "4da47f5c-f38d-4519-89b3-82feda6e81ab", + }, + body: JSON.stringify({ + query, + variables: { + workflowId: "597871bb-6b0b-4cef-9771-05514dee60cd", + owner, + repo, + pr_number, + }, + }), + } + ) + + const raw = await res.text() + const trimmed = raw.trim() + let data: any = null + + if (trimmed) { + try { + data = JSON.parse(trimmed) + } catch { + data = null + } + } + + if (!res.ok) { + const upstreamMessage = + data?.errors?.map((error: any) => error?.message).filter(Boolean).join("; ") || + data?.message || + (trimmed.startsWith("<") + ? "Lamatic returned HTML instead of JSON." + : "Lamatic returned an unsuccessful response.") + + console.error("Lamatic workflow request failed", { + status: res.status, + message: upstreamMessage, + }) + + return NextResponse.json( + { error: `Lamatic request failed (${res.status}): ${upstreamMessage}` }, + { status: 502 } + ) + } + + if (!data || typeof data !== "object") { + console.error("Lamatic returned a non-JSON response", { + status: res.status, + }) + + return NextResponse.json( + { error: "Lamatic returned an invalid non-JSON response." }, + { status: 502 } + ) + } + + if (Array.isArray(data.errors) && data.errors.length > 0) { + const message = data.errors + .map((error: any) => error?.message) + .filter(Boolean) + .join("; ") + + console.error("Lamatic GraphQL errors", { message }) + + return NextResponse.json( + { error: message || "Lamatic returned GraphQL errors." }, + { status: 502 } + ) + } + + const result = data?.data?.executeWorkflow?.result + + return NextResponse.json({ + bugs: Array.isArray(result?.bugs) ? result.bugs : [], + security: Array.isArray(result?.security) ? result.security : [], + style: Array.isArray(result?.style) ? result.style : [], + summary: typeof result?.summary === "string" ? result.summary : "", + }) + } catch (error) { + console.error("Review route failed", error) + + return NextResponse.json( + { error: "The review route failed before it could complete the request." }, + { status: 500 } + ) + } +} diff --git a/kits/agentic/code-review/app/globals.css b/kits/agentic/code-review/app/globals.css new file mode 100644 index 00000000..10f1f3ea --- /dev/null +++ b/kits/agentic/code-review/app/globals.css @@ -0,0 +1,11 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: sans-serif; + background: #fff; + color: #111; +} \ No newline at end of file diff --git a/kits/agentic/code-review/app/layout.tsx b/kits/agentic/code-review/app/layout.tsx new file mode 100644 index 00000000..98fe75cf --- /dev/null +++ b/kits/agentic/code-review/app/layout.tsx @@ -0,0 +1,19 @@ +import type { Metadata } from "next" +import "./globals.css" + +export const metadata: Metadata = { + title: "Code Review Agent", + description: "AI-powered GitHub PR code review", +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/kits/agentic/code-review/app/page.tsx b/kits/agentic/code-review/app/page.tsx new file mode 100644 index 00000000..d46ac4c8 --- /dev/null +++ b/kits/agentic/code-review/app/page.tsx @@ -0,0 +1,729 @@ +"use client" + +import { type FormEvent, type HTMLAttributes, type ReactNode, useState } from "react" + +type Finding = { line: string; issue: string; severity: string } +type StyleItem = string | { suggestion?: string; issue?: string } +type ReviewResult = { + summary: string + bugs: Finding[] + security: Finding[] + style: StyleItem[] +} + +export default function Home() { + const [owner, setOwner] = useState("") + const [repo, setRepo] = useState("") + const [prNumber, setPrNumber] = useState("") + const [loading, setLoading] = useState(false) + const [result, setResult] = useState(null) + const [error, setError] = useState("") + + async function handleReview() { + if (!owner || !repo || !prNumber) return + setLoading(true) + setError("") + setResult(null) + try { + const res = await fetch("/api/review", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ owner, repo, pr_number: prNumber }), + }) + + const raw = await res.text() + const trimmed = raw.trim() + let data: any = null + + if (trimmed) { + try { + data = JSON.parse(trimmed) + } catch { + data = null + } + } + + if (!res.ok) { + if (typeof data?.error === "string" && data.error) { + throw new Error(data.error) + } + + if (trimmed.startsWith("<")) { + throw new Error( + `The review API returned HTML instead of JSON (status ${res.status}). The server route likely failed before it could return a structured response.` + ) + } + + throw new Error(`Review request failed with status ${res.status}.`) + } + + if (!data || typeof data !== "object") { + throw new Error( + "The review API returned an invalid response. The server may be failing before it can send JSON." + ) + } + + setResult({ + summary: typeof data.summary === "string" ? data.summary : "", + bugs: Array.isArray(data.bugs) ? data.bugs : [], + security: Array.isArray(data.security) ? data.security : [], + style: Array.isArray(data.style) ? data.style : [], + }) + } catch (e: any) { + setError(e.message) + } finally { + setLoading(false) + } + } + + const bugCount = result?.bugs?.length ?? 0 + const securityCount = result?.security?.length ?? 0 + const styleCount = result?.style?.length ?? 0 + const target = owner && repo ? `${owner}/${repo}` : "owner/repo" + const pull = prNumber ? `#${prNumber}` : "#PR" + + return ( + <> +
+
+
+
+ +
+
+ AI Pull Request Review +

Elegant PR reviews, shaped like a modern developer tool.

+

+ Code Review Agent keeps the same Lamatic-backed review flow and + re-presents the output as a cleaner summary plus triage-ready bug, + security, and style cards. +

+
+ + Endpoint + /api/review + + + Output + Summary · Bugs · Security · Style + + + Target + {target} {pull} + +
+
+ +
+
+
+ Review Input +

Analyze a GitHub pull request

+
+ Live Flow +
+ +
) => { + event.preventDefault() + void handleReview() + }} + > + + + +
+ +
+ + +
+ Three inputs in, structured findings out. + + API behavior is unchanged. +
+
+ + {error ? ( +
+ Request failed +

{error}

+
+ ) : null} + + {loading ? ( + + ) : result ? ( +
+
+ + + + +
+ +
+
+
+
+ Summary +

PR overview

+
+
+ {target} + {pull} +
+
+

{result.summary || "No high-level summary was returned for this pull request."}

+
+ +
+ + {bugCount ? ( + result.bugs.map((item, index) => ) + ) : ( +
No bug-level issues were flagged in this review.
+ )} +
+ + + {securityCount ? ( + result.security.map((item, index) => ) + ) : ( +
No security issues were surfaced in this review.
+ )} +
+ + + {styleCount ? ( + result.style.map((item, index) => ( + + )) + ) : ( +
No style suggestions were returned for this review.
+ )} +
+
+
+ ) : ( + + )} +
+
+ + + + ) +} + +function Field({ + className, + label, + placeholder, + value, + onChange, + inputMode, +}: { + className: string + label: string + placeholder: string + value: string + onChange: (value: string) => void + inputMode?: HTMLAttributes["inputMode"] +}) { + return ( + + ) +} + +function StatCard({ label, value, accent }: { label: string; value: string; accent: string }) { + return ( +
+ + {value} + {label} +
+ ) +} + +function ResultCard({ + title, + description, + count, + accent, + children, +}: { + title: string + description: string + count: number + accent: string + children: ReactNode +}) { + return ( +
+
+
+
+

{title}

+

{description}

+
+ {count} +
+
{children}
+
+ ) +} + +function FindingItem({ finding, accent }: { finding: Finding; accent: string }) { + const badge = severityTone(finding.severity) + return ( +
+
+
+ + + {finding.severity || "Unknown"} + +
+ Line {finding.line || "?"} +
+

{finding.issue}

+
+ ) +} + +function StyleSuggestionCard({ text }: { text: string }) { + return ( +
+ +

{text}

+
+ ) +} + +function EmptyState() { + return ( +
+ Ready +

The review surface is standing by.

+

+ Enter a GitHub owner, repository, and pull request number to render the + review in a more structured workspace. +

+
+ + + +
+
+ ) +} + +function Preview({ title, text }: { title: string; text: string }) { + return ( +
+
+

{title}

+

{text}

+
+ ) +} + +function LoadingState() { + return ( +
+
+ +
+ Review In Progress + Scanning the pull request and assembling findings +
+
+
+ {Array.from({ length: 4 }).map((_, index) => ( +
+
+
+
+ ))} +
+
+
+
+
+
+
+
+ {Array.from({ length: 3 }).map((_, index) => ( +
+
+
+
+
+
+ ))} +
+
+ ) +} + +function severityTone(severity: string) { + switch (severity?.toLowerCase()) { + case "critical": + return { background: "rgba(239,68,68,.2)", color: "#fecaca", border: "1px solid rgba(248,113,113,.38)" } + case "high": + return { background: "rgba(248,113,113,.15)", color: "#fca5a5", border: "1px solid rgba(248,113,113,.28)" } + case "medium": + return { background: "rgba(250,204,21,.16)", color: "#fde68a", border: "1px solid rgba(250,204,21,.3)" } + case "low": + return { background: "rgba(148,163,184,.16)", color: "#cbd5e1", border: "1px solid rgba(148,163,184,.24)" } + default: + return { background: "rgba(96,165,250,.16)", color: "#bfdbfe", border: "1px solid rgba(96,165,250,.24)" } + } +} diff --git a/kits/agentic/code-review/components.json b/kits/agentic/code-review/components.json new file mode 100644 index 00000000..4ee62ee1 --- /dev/null +++ b/kits/agentic/code-review/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/kits/agentic/code-review/components/header.tsx b/kits/agentic/code-review/components/header.tsx new file mode 100644 index 00000000..2c2fcc5a --- /dev/null +++ b/kits/agentic/code-review/components/header.tsx @@ -0,0 +1,47 @@ +import Link from "next/link" +import { FileText, Github } from "lucide-react" +import Image from "next/image" + +export function Header() { + return ( +
+
+ +
+ Lamatic Logo +

+ Lamatic + Generator +

+
+ +
+ + + Docs + + + + GitHub + +
+
+
+ ) +} diff --git a/kits/agentic/code-review/components/theme-provider.tsx b/kits/agentic/code-review/components/theme-provider.tsx new file mode 100644 index 00000000..55c2f6eb --- /dev/null +++ b/kits/agentic/code-review/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/kits/agentic/code-review/components/ui/accordion.tsx b/kits/agentic/code-review/components/ui/accordion.tsx new file mode 100644 index 00000000..e538a33b --- /dev/null +++ b/kits/agentic/code-review/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +'use client' + +import * as React from 'react' +import * as AccordionPrimitive from '@radix-ui/react-accordion' +import { ChevronDownIcon } from 'lucide-react' + +import { cn } from '@/lib/utils' + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180', + className, + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/kits/agentic/code-review/components/ui/alert-dialog.tsx b/kits/agentic/code-review/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..97044526 --- /dev/null +++ b/kits/agentic/code-review/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +'use client' + +import * as React from 'react' +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' + +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/kits/agentic/code-review/components/ui/alert.tsx b/kits/agentic/code-review/components/ui/alert.tsx new file mode 100644 index 00000000..e6751abe --- /dev/null +++ b/kits/agentic/code-review/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const alertVariants = cva( + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + { + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<'div'> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/kits/agentic/code-review/components/ui/aspect-ratio.tsx b/kits/agentic/code-review/components/ui/aspect-ratio.tsx new file mode 100644 index 00000000..40bb1208 --- /dev/null +++ b/kits/agentic/code-review/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio' + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/kits/agentic/code-review/components/ui/avatar.tsx b/kits/agentic/code-review/components/ui/avatar.tsx new file mode 100644 index 00000000..aa98465a --- /dev/null +++ b/kits/agentic/code-review/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +'use client' + +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' + +import { cn } from '@/lib/utils' + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/kits/agentic/code-review/components/ui/badge.tsx b/kits/agentic/code-review/components/ui/badge.tsx new file mode 100644 index 00000000..fc4126b7 --- /dev/null +++ b/kits/agentic/code-review/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const badgeVariants = cva( + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + secondary: + 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + destructive: + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<'span'> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : 'span' + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/kits/agentic/code-review/components/ui/breadcrumb.tsx b/kits/agentic/code-review/components/ui/breadcrumb.tsx new file mode 100644 index 00000000..1750ff26 --- /dev/null +++ b/kits/agentic/code-review/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { ChevronRight, MoreHorizontal } from 'lucide-react' + +import { cn } from '@/lib/utils' + +function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { + return