feat: Add code-review-agent AgentKit#83
Conversation
📝 WalkthroughWalkthroughAdded a new code review agent kit in Changes
Sequence DiagramsequenceDiagram
actor User
participant Client as Next.js Client Page
participant Server as API Route<br/>/api/review
participant Lamatic as Lamatic API
User->>Client: Enter owner, repo, pr_number
User->>Client: Click "Review PR"
Client->>Client: Validate inputs
Client->>Server: POST /api/review<br/>(owner, repo, pr_number)
Server->>Server: Validate env vars<br/>(LAMATIC_API_KEY)
Server->>Lamatic: POST executeWorkflow<br/>(workflow ID, payload)
Lamatic-->>Server: GraphQL response<br/>(bugs, security, style)
Server->>Server: Parse & extract<br/>result arrays
Server-->>Client: JSON<br/>(bugs, security,<br/>style, summary)
Client->>Client: Update state<br/>(loading → result)
Client->>User: Render review results<br/>with cards & triage counts
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (13)
kits/agentic/code-review/flows/agentic-generate-content/README.md-1-5 (1)
1-5:⚠️ Potential issue | 🟡 MinorREADME scope looks inconsistent with this kit’s purpose.
Line 1 and Line 5 describe a generic “Generate Content” flow, which doesn’t match a code-review agent narrative. Please align the title/overview with the actual kit behavior to avoid onboarding confusion.
kits/agentic/code-review/components/ui/progress.tsx-25-25 (1)
25-25:⚠️ Potential issue | 🟡 MinorClamp progress value before building the transform string.
On Line 25, values greater than 100 can generate invalid CSS (e.g.,
translateX(--20%)). Normalize to[0, 100]first.Suggested patch
function Progress({ className, value, ...props }: React.ComponentProps<typeof ProgressPrimitive.Root>) { + const normalizedValue = Math.min(100, Math.max(0, value ?? 0)) + return ( @@ <ProgressPrimitive.Indicator data-slot="progress-indicator" className="bg-primary h-full w-full flex-1 transition-all" - style={{ transform: `translateX(-${100 - (value || 0)}%)` }} + style={{ transform: `translateX(-${100 - normalizedValue}%)` }} />kits/agentic/code-review/.env.example-1-4 (1)
1-4:⚠️ Potential issue | 🟡 MinorFix dotenv formatting to satisfy linter expectations.
Line 1–Line 4 use spaces around
=, and the file should end with a newline. Reordering keys to match linter ordering avoids recurring CI noise.Suggested patch
-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" +AGENTIC_GENERATE_CONTENT="AGENTIC_GENERATE_CONTENT Flow ID" +LAMATIC_API_KEY="LAMATIC_API_KEY" +LAMATIC_API_URL="LAMATIC_API_URL" +LAMATIC_PROJECT_ID="LAMATIC_PROJECT_ID"kits/agentic/code-review/components/ui/form.tsx-28-30 (1)
28-30:⚠️ Potential issue | 🟡 MinorGuard checks are ineffective and come after usage.
FormFieldContextandFormItemContextinitialize with casted empty-object defaults, making the falsy check on line 52 unreachable. Additionally,fieldContext.nameis accessed on lines 49–50 before the guard check, anditemContext.idis accessed on line 56 with no guard at all. If these hooks are called outside their providers,idwill beundefined, degrading aria attributes and IDs to "undefined" instead of failing loudly. Initialize both contexts asnulland add a guard foritemContext.Also applies to: lines 45–56, 72–74
kits/agentic/code-review/app/page.tsx-681-687 (1)
681-687:⚠️ Potential issue | 🟡 MinorAnnounce review progress to assistive tech.
The loading banner is visual only right now. Add a live status role so screen readers get notified when a review starts.
♿ Small accessibility improvement
- <div className="cr-loading-banner"> + <div className="cr-loading-banner" role="status" aria-live="polite"> <span className="cr-spinner" /> <div> <span className="cr-section-label">Review In Progress</span>kits/agentic/code-review/app/api/review/route.ts-27-37 (1)
27-37:⚠️ Potential issue | 🟡 MinorValidate
pr_numberat the API boundary.Right now any non-empty string is forwarded upstream, so obvious bad input becomes a downstream Lamatic failure instead of a fast
400. This route is the trust boundary and should reject non-numeric PR numbers before making the external call.🧪 Tighten request validation
if (!owner || !repo || !pr_number) { return NextResponse.json( { error: "Owner, repo, and PR number are required." }, { status: 400 } ) } + + if (!/^[1-9]\d*$/.test(pr_number)) { + return NextResponse.json( + { error: "PR number must be a positive integer." }, + { status: 400 } + ) + }kits/agentic/code-review/components/ui/empty.tsx-71-82 (1)
71-82:⚠️ Potential issue | 🟡 MinorType mismatch: props typed as
<p>but renders<div>.
EmptyDescriptionacceptsReact.ComponentProps<'p'>but renders a<div>. This could cause confusion and potentially invalid prop usage.🔧 Proposed fix - either change the type or the element
Option 1: Match the element to the type:
function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) { return ( - <div + <p data-slot="empty-description" className={cn( 'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4', className, )} {...props} /> ) }Option 2: Match the type to the element:
-function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) { +function EmptyDescription({ className, ...props }: React.ComponentProps<'div'>) {kits/agentic/code-review/components/ui/chart.tsx-235-239 (1)
235-239:⚠️ Potential issue | 🟡 MinorPotential falsy value handling issue.
Line 235 uses
{item.value && ...}which will hide the value whenitem.valueis0. If zero is a valid chart value, this condition should be revised.Proposed fix
- {item.value && ( + {item.value !== undefined && item.value !== null && ( <span className="text-foreground font-mono font-medium tabular-nums"> {item.value.toLocaleString()} </span> )}kits/agentic/code-review/components/ui/button-group.tsx-1-5 (1)
1-5:⚠️ Potential issue | 🟡 MinorMissing
'use client'directive.This module uses
@radix-ui/react-slotwhich requires client-side rendering. Add the directive for consistency with other UI components.Proposed fix
+'use client' + import { Slot } from '@radix-ui/react-slot' import { cva, type VariantProps } from 'class-variance-authority'kits/agentic/code-review/components/ui/navigation-menu.tsx-1-6 (1)
1-6:⚠️ Potential issue | 🟡 MinorMissing
'use client'directive.This module wraps Radix UI primitives that require client-side rendering. Add the directive at the top of the file for consistency with other UI components in this kit (e.g.,
table.tsx,field.tsx).Proposed fix
+'use client' + import * as React from 'react' import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'kits/agentic/code-review/components/ui/sidebar.tsx-282-303 (1)
282-303:⚠️ Potential issue | 🟡 MinorDefault the raw button render paths to
type="button".These controls fall back to
<button type="submit">inside a form today, so clicking the rail or menu actions can submit an unrelated form unexpectedly.Suggested pattern
<button + type="button" ...<Comp + type={asChild ? undefined : 'button'} ...Apply the first pattern to
SidebarRail, and the second toSidebarGroupAction,SidebarMenuButton, andSidebarMenuAction.Also applies to: 417-437, 498-523, 548-577
kits/agentic/code-review/components/ui/input-group.tsx-70-75 (1)
70-75:⚠️ Potential issue | 🟡 MinorFocus the grouped textarea too.
querySelector('input')never findsInputGroupTextarea, so clicking the addon stops working for textarea-based groups.Suggested fix
- e.currentTarget.parentElement?.querySelector('input')?.focus() + e.currentTarget.parentElement + ?.querySelector<HTMLElement>('[data-slot="input-group-control"]') + ?.focus()kits/agentic/code-review/components/ui/input-group.tsx-3-10 (1)
3-10:⚠️ Potential issue | 🟡 MinorAdd missing React import.
This file uses
React.ComponentPropsin type signatures (6 occurrences) but does not importReact. While TypeScript may not error with the currentstrict: falseconfiguration, this violates the project's consistent pattern where component files import React. Add the import:+import type * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority'
🧹 Nitpick comments (21)
kits/agentic/code-review/components/ui/carousel.tsx (1)
78-89: Keyboard navigation doesn't adapt to vertical orientation.Arrow left/right are used regardless of the carousel's orientation. For vertical carousels, users typically expect ArrowUp/ArrowDown to navigate slides, which would improve accessibility.
♻️ Proposed enhancement for orientation-aware keyboard navigation
const handleKeyDown = React.useCallback( (event: React.KeyboardEvent<HTMLDivElement>) => { - if (event.key === 'ArrowLeft') { + const prevKeys = orientation === 'horizontal' ? ['ArrowLeft'] : ['ArrowUp'] + const nextKeys = orientation === 'horizontal' ? ['ArrowRight'] : ['ArrowDown'] + + if (prevKeys.includes(event.key)) { event.preventDefault() scrollPrev() - } else if (event.key === 'ArrowRight') { + } else if (nextKeys.includes(event.key)) { event.preventDefault() scrollNext() } }, - [scrollPrev, scrollNext], + [orientation, scrollPrev, scrollNext], )kits/agentic/code-review/components/ui/sonner.tsx (1)
6-20: MergeclassNameandstylebefore spreading props.Because
{...props}comes last on Lines 10-20, any caller-providedclassNameorstylereplaces the wrapper's base toaster classes and CSS variable defaults instead of extending them.Suggested fix
'use client' import { useTheme } from 'next-themes' import { Toaster as Sonner, ToasterProps } from 'sonner' +import { cn } from '@/lib/utils' -const Toaster = ({ ...props }: ToasterProps) => { +const Toaster = ({ className, style, ...props }: ToasterProps) => { const { theme = 'system' } = useTheme() return ( <Sonner theme={theme as ToasterProps['theme']} - className="toaster group" - style={ - { + className={cn('toaster group', className)} + style={{ '--normal-bg': 'var(--popover)', '--normal-text': 'var(--popover-foreground)', '--normal-border': 'var(--border)', - } as React.CSSProperties - } + ...style, + } as React.CSSProperties} {...props} /> ) }kits/agentic/code-review/components/ui/collapsible.tsx (1)
11-30: Forward refs through the wrapped Radix primitives.
CollapsibleTriggerandCollapsibleContentare plain function components now, so arefpassed to these exports stops at the wrapper instead of reaching the underlying Radix primitive. That narrows the API surface for focus and measurement use cases.kits/agentic/code-review/flows/agentic-generate-content/meta.json (1)
2-8: Align flow metadata with the code-review kit purpose.Line 2 and Line 7 still describe a generic content generation scenario (“poem on AI”). Recommend updating this metadata so sample/test execution validates code-review behavior.
Suggested metadata update
- "name": "1. Agentic Generation - Generate Content", + "name": "1. Agentic Generation - Code Review", @@ - "instructions": "write me a poem on AI" + "instructions": "Review this GitHub pull request and report critical, major, and minor issues with line references."kits/agentic/code-review/components/ui/use-mobile.tsx (1)
9-15: Usemql.matchesdirectly for a single source of truth.Line 11 and Line 14 recompute via
window.innerWidth; since you already havemql, updating frommql.matcheskeeps logic tighter and avoids drift.Refactor suggestion
React.useEffect(() => { const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + setIsMobile(mql.matches) } mql.addEventListener('change', onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + setIsMobile(mql.matches) return () => mql.removeEventListener('change', onChange) }, [])kits/agentic/code-review/components/ui/kbd.tsx (1)
18-24: Use a<div>forKbdGroupto match its props and semantics.
KbdGroupis typed withdivprops but currently renders<kbd>. A non-semantic container is a better fit for grouping keycaps.Suggested patch
function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) { return ( - <kbd + <div data-slot="kbd-group" className={cn('inline-flex items-center gap-1', className)} {...props} - /> + /> ) }kits/agentic/code-review/components/ui/slider.tsx (1)
16-23: Consider defaulting to a single thumb when no values are provided.Current fallback (
[min, max]) creates two thumbs by default. For a base slider primitive,[min]is usually the safer default unless range mode is explicit.Suggested patch
const _values = React.useMemo( () => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue - : [min, max], + : [min], [value, defaultValue, min, max], )kits/agentic/code-review/lib/lamatic-client.ts (1)
1-20: Fence this SDK helper to server code.This module reads private env vars from a shared
lib/path. Addingserver-onlyhere makes accidental client imports fail fast instead of bundling a broken secret-dependent module.Suggested change
+import "server-only"; import { Lamatic } from "lamatic"; import {config} from '../orchestrate.js'kits/agentic/code-review/actions/orchestrate.ts (1)
19-29: Don’t select the workflow by object insertion order.
Object.keys(flows)[0]makes execution depend on config ordering. As soon as another flow is added or the object is reordered,generateContent()can silently start calling a different workflow. Resolve a named flow explicitly or pass the target flow key/id into this action.kits/agentic/code-review/app/page.tsx (1)
237-557: Move the page-scoped CSS out of the component body.This
style jsx globalblock is now larger than the render logic and applies globally. A CSS module or colocated stylesheet would keep the page readable and reduce the chance of accidental style bleed if another page starts reusingcr-*class names.kits/agentic/code-review/components/ui/tooltip.tsx (1)
21-29: Consider extracting the provider for better control.Each
Tooltipinstance creates its ownTooltipProvider, which works for self-contained usage but prevents shared delay/skip behavior across tooltips. If multiple tooltips are used on a page, consider wrapping them with a singleTooltipProviderat a higher level and removing the nested provider here.This is acceptable for isolated use cases but worth noting for scalability.
kits/agentic/code-review/components/ui/pagination.tsx (1)
9-9: Remove unusedButtonimport.Only
buttonVariantsis used;Buttonis imported but never referenced.🧹 Proposed fix
-import { Button, buttonVariants } from '@/components/ui/button' +import { buttonVariants } from '@/components/ui/button'kits/agentic/code-review/components/ui/hover-card.tsx (1)
28-40:data-sloton Portal has no effect.
HoverCardPrimitive.Portaldoesn't render a DOM element—it's a React portal container. Thedata-slot="hover-card-portal"attribute won't appear in the DOM and serves no purpose.🧹 Proposed fix
<HoverCardPrimitive.Portal data-slot="hover-card-portal"> + <HoverCardPrimitive.Portal>kits/agentic/code-review/components/ui/dialog.tsx (1)
57-59:data-sloton DialogPortal has no effect.Similar to the hover-card,
DialogPrimitive.Portaldoesn't render a DOM element, so thedata-slotattribute passed here won't appear in the DOM.🧹 Proposed fix
- <DialogPortal data-slot="dialog-portal"> + <DialogPortal>kits/agentic/code-review/components/ui/item.tsx (1)
43-43: Minor: trailing whitespace in class string.The
defaultsize variant has a trailing space. Whilecn()handles this gracefully, it's cleaner to remove it.🧹 Proposed fix
- default: 'p-4 gap-4 ', + default: 'p-4 gap-4',kits/agentic/code-review/components/ui/input-otp.tsx (1)
69-75: Consider addingaria-hiddento decorative separator.The separator is purely visual. Adding
aria-hidden="true"would prevent screen readers from announcing it.♿ Proposed fix
function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) { return ( - <div data-slot="input-otp-separator" role="separator" {...props}> + <div data-slot="input-otp-separator" role="separator" aria-hidden="true" {...props}> <MinusIcon /> </div> ) }kits/agentic/code-review/components/ui/drawer.tsx (1)
53-55:data-sloton DrawerPortal has no effect.Same as Dialog and HoverCard—Portal components don't render DOM elements, so this attribute won't appear in the DOM.
🧹 Proposed fix
- <DrawerPortal data-slot="drawer-portal"> + <DrawerPortal>kits/agentic/code-review/components/ui/field.tsx (1)
128-139: Duplicatedata-slotvalue withFieldLabel.
FieldTitleusesdata-slot="field-label"(Line 131), which is the same value used byFieldLabel(Line 116). This could cause unintended styling overlap or confusion when querying by slot. Consider using a distinct value like"field-title".Proposed fix
function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) { return ( <div - data-slot="field-label" + data-slot="field-title" className={cn(kits/agentic/code-review/components/ui/chart.tsx (1)
72-103: AcknowledgedangerouslySetInnerHTMLusage for CSS injection.The static analysis flagged this pattern. While the
configis developer-controlled (not user input), ensuring thatChartConfigvalues are never populated from untrusted sources is important. The current implementation is safe for typical use where config is statically defined, but consider adding a brief comment documenting this assumption.Optional: Add documentation comment
+// IMPORTANT: ChartConfig values (keys and colors) must come from trusted sources. +// Do not populate config with user-provided input to avoid CSS injection. const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {kits/agentic/code-review/components/ui/command.tsx (1)
45-60:DialogHeaderplaced outsideDialogContent.The
DialogHeader(Lines 47-50) is rendered as a sibling toDialogContentrather than inside it. While thesr-onlyclass hides it visually, the accessible name/description may not be properly associated with the dialog content. Consider moving it insideDialogContentor usingaria-labelledby/aria-describedbyattributes on the content.Proposed fix
return ( <Dialog {...props}> - <DialogHeader className="sr-only"> - <DialogTitle>{title}</DialogTitle> - <DialogDescription>{description}</DialogDescription> - </DialogHeader> <DialogContent className={cn('overflow-hidden p-0', className)} showCloseButton={showCloseButton} > + <DialogHeader className="sr-only"> + <DialogTitle>{title}</DialogTitle> + <DialogDescription>{description}</DialogDescription> + </DialogHeader> <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> </DialogContent> </Dialog> )kits/agentic/code-review/components/ui/alert-dialog.tsx (1)
121-143: Missingdata-slotattributes onAlertDialogActionandAlertDialogCancel.All other components in this file include
data-slotattributes for consistent instrumentation, but these two are missing them.Proposed fix
function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) { return ( <AlertDialogPrimitive.Action + data-slot="alert-dialog-action" className={cn(buttonVariants(), className)} {...props} /> ) } function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) { return ( <AlertDialogPrimitive.Cancel + data-slot="alert-dialog-cancel" className={cn(buttonVariants({ variant: 'outline' }), className)} {...props} /> ) }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0296a6ec-c53d-49d5-9acc-97b5446eeb42
⛔ Files ignored due to path filters (3)
kits/agentic/code-review/.next-dev-3001.err.logis excluded by!**/*.logkits/agentic/code-review/.next-dev-3001.logis excluded by!**/*.logkits/agentic/code-review/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (80)
kits/agentic/code-review/.env.examplekits/agentic/code-review/.gitignorekits/agentic/code-review/README.mdkits/agentic/code-review/actions/orchestrate.tskits/agentic/code-review/app/api/review/route.tskits/agentic/code-review/app/globals.csskits/agentic/code-review/app/layout.tsxkits/agentic/code-review/app/page.tsxkits/agentic/code-review/components.jsonkits/agentic/code-review/components/header.tsxkits/agentic/code-review/components/theme-provider.tsxkits/agentic/code-review/components/ui/accordion.tsxkits/agentic/code-review/components/ui/alert-dialog.tsxkits/agentic/code-review/components/ui/alert.tsxkits/agentic/code-review/components/ui/aspect-ratio.tsxkits/agentic/code-review/components/ui/avatar.tsxkits/agentic/code-review/components/ui/badge.tsxkits/agentic/code-review/components/ui/breadcrumb.tsxkits/agentic/code-review/components/ui/button-group.tsxkits/agentic/code-review/components/ui/button.tsxkits/agentic/code-review/components/ui/calendar.tsxkits/agentic/code-review/components/ui/card.tsxkits/agentic/code-review/components/ui/carousel.tsxkits/agentic/code-review/components/ui/chart.tsxkits/agentic/code-review/components/ui/checkbox.tsxkits/agentic/code-review/components/ui/collapsible.tsxkits/agentic/code-review/components/ui/command.tsxkits/agentic/code-review/components/ui/context-menu.tsxkits/agentic/code-review/components/ui/dialog.tsxkits/agentic/code-review/components/ui/drawer.tsxkits/agentic/code-review/components/ui/dropdown-menu.tsxkits/agentic/code-review/components/ui/empty.tsxkits/agentic/code-review/components/ui/field.tsxkits/agentic/code-review/components/ui/form.tsxkits/agentic/code-review/components/ui/hover-card.tsxkits/agentic/code-review/components/ui/input-group.tsxkits/agentic/code-review/components/ui/input-otp.tsxkits/agentic/code-review/components/ui/input.tsxkits/agentic/code-review/components/ui/item.tsxkits/agentic/code-review/components/ui/kbd.tsxkits/agentic/code-review/components/ui/label.tsxkits/agentic/code-review/components/ui/menubar.tsxkits/agentic/code-review/components/ui/navigation-menu.tsxkits/agentic/code-review/components/ui/pagination.tsxkits/agentic/code-review/components/ui/popover.tsxkits/agentic/code-review/components/ui/progress.tsxkits/agentic/code-review/components/ui/radio-group.tsxkits/agentic/code-review/components/ui/resizable.tsxkits/agentic/code-review/components/ui/scroll-area.tsxkits/agentic/code-review/components/ui/select.tsxkits/agentic/code-review/components/ui/separator.tsxkits/agentic/code-review/components/ui/sheet.tsxkits/agentic/code-review/components/ui/sidebar.tsxkits/agentic/code-review/components/ui/skeleton.tsxkits/agentic/code-review/components/ui/slider.tsxkits/agentic/code-review/components/ui/sonner.tsxkits/agentic/code-review/components/ui/spinner.tsxkits/agentic/code-review/components/ui/switch.tsxkits/agentic/code-review/components/ui/table.tsxkits/agentic/code-review/components/ui/tabs.tsxkits/agentic/code-review/components/ui/textarea.tsxkits/agentic/code-review/components/ui/toast.tsxkits/agentic/code-review/components/ui/toaster.tsxkits/agentic/code-review/components/ui/toggle-group.tsxkits/agentic/code-review/components/ui/toggle.tsxkits/agentic/code-review/components/ui/tooltip.tsxkits/agentic/code-review/components/ui/use-mobile.tsxkits/agentic/code-review/components/ui/use-toast.tskits/agentic/code-review/config.jsonkits/agentic/code-review/flows/agentic-generate-content/README.mdkits/agentic/code-review/flows/agentic-generate-content/config.jsonkits/agentic/code-review/flows/agentic-generate-content/inputs.jsonkits/agentic/code-review/flows/agentic-generate-content/meta.jsonkits/agentic/code-review/hooks/use-mobile.tskits/agentic/code-review/hooks/use-toast.tskits/agentic/code-review/lib/lamatic-client.tskits/agentic/code-review/lib/utils.tskits/agentic/code-review/next.config.mjskits/agentic/code-review/package.jsonkits/agentic/code-review/tsconfig.json
| # env files | ||
| .env |
There was a problem hiding this comment.
Ignore .env.local here too.
Line 20 only ignores .env, but this kit's setup guide asks users to create .env.local with Lamatic credentials. That leaves the most likely secrets file trackable unless a parent .gitignore happens to catch it.
Suggested fix
# env files
.env
+.env.local
+.env.*.local| console.log("[v0] Generating content with:", { inputType, instructions }) | ||
|
|
There was a problem hiding this comment.
Stop logging raw review payloads on the server.
These statements write instructions, derived workflow inputs, and the full Lamatic response into server logs. In a PR-review flow that can include repository metadata and generated findings, so this is sensitive data that should be redacted or dropped.
🛡️ Minimal redaction-oriented change
- console.log("[v0] Generating content with:", { inputType, instructions })
+ console.log("[code-review-agent] Generating content", { inputType })
@@
- console.log("[v0] Using workflow:", flow.name, flow.workflowId);
+ console.log("[code-review-agent] Using workflow", {
+ workflowId: flow.workflowId,
+ workflowName: flow.name,
+ })
@@
- console.log("[v0] Sending inputs:", inputs)
+ console.log("[code-review-agent] Sending workflow request", {
+ workflowId: flow.workflowId,
+ inputType,
+ })
@@
- console.log("[v0] Raw response:", resData)
@@
- console.error("[v0] Generation error:", error)
+ console.error(
+ "[code-review-agent] Generation error",
+ error instanceof Error ? error.message : String(error),
+ )Also applies to: 29-29, 46-52, 66-66
| // Parse the answer from resData?.output.answer | ||
| const answer = resData?.result?.answer | ||
|
|
||
| if (!answer) { | ||
| throw new Error("No answer found in response") | ||
| } |
There was a problem hiding this comment.
Only treat null/undefined as a missing answer.
if (!answer) rejects valid falsy outputs such as "", 0, false, or an empty JSON value. For a generic text | image | json helper, that turns legitimate workflow results into errors.
✅ Safer missing-value check
- if (!answer) {
+ if (answer === undefined || answer === null) {
throw new Error("No answer found in response")
}📝 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.
| // Parse the answer from resData?.output.answer | |
| const answer = resData?.result?.answer | |
| if (!answer) { | |
| throw new Error("No answer found in response") | |
| } | |
| // Parse the answer from resData?.output.answer | |
| const answer = resData?.result?.answer | |
| if (answer === undefined || answer === null) { | |
| throw new Error("No answer found in response") | |
| } |
| 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", |
There was a problem hiding this comment.
Remove tenant-specific Lamatic wiring from source.
This route hardcodes the Lamatic subdomain, project ID, and workflow ID. Any deployment with a different account/API key will still post PR data to this tenant, which is both a cross-environment data leak and a functional blocker. These values need to come from env or shared config instead of source.
🔐 Suggested env-backed wiring
- if (!process.env.LAMATIC_API_KEY) {
+ const lamaticApiKey = process.env.LAMATIC_API_KEY
+ const lamaticGraphqlUrl = process.env.LAMATIC_GRAPHQL_URL
+ const lamaticProjectId = process.env.LAMATIC_PROJECT_ID
+ const lamaticWorkflowId = process.env.LAMATIC_WORKFLOW_ID
+
+ if (!lamaticApiKey || !lamaticGraphqlUrl || !lamaticProjectId || !lamaticWorkflowId) {
return NextResponse.json(
- { error: "LAMATIC_API_KEY is not configured on the server." },
+ { error: "Lamatic server configuration is incomplete." },
{ status: 500 }
)
}
@@
- "https://soumiksorganization573-codereviewagent135.lamatic.dev/graphql",
+ lamaticGraphqlUrl,
{
method: "POST",
headers: {
- Authorization: `Bearer ${process.env.LAMATIC_API_KEY}`,
+ Authorization: `Bearer ${lamaticApiKey}`,
"Content-Type": "application/json",
- "x-project-id": "4da47f5c-f38d-4519-89b3-82feda6e81ab",
+ "x-project-id": lamaticProjectId,
},
body: JSON.stringify({
query,
variables: {
- workflowId: "597871bb-6b0b-4cef-9771-05514dee60cd",
+ workflowId: lamaticWorkflowId,
owner,
repo,
pr_number,📝 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.
| 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", | |
| const lamaticApiKey = process.env.LAMATIC_API_KEY | |
| const lamaticGraphqlUrl = process.env.LAMATIC_GRAPHQL_URL | |
| const lamaticProjectId = process.env.LAMATIC_PROJECT_ID | |
| const lamaticWorkflowId = process.env.LAMATIC_WORKFLOW_ID | |
| if (!lamaticApiKey || !lamaticGraphqlUrl || !lamaticProjectId || !lamaticWorkflowId) { | |
| return NextResponse.json( | |
| { error: "Lamatic server configuration is incomplete." }, | |
| { status: 500 } | |
| ) | |
| } | |
| const res = await fetch( | |
| lamaticGraphqlUrl, | |
| { | |
| method: "POST", | |
| headers: { | |
| Authorization: `Bearer ${lamaticApiKey}`, | |
| "Content-Type": "application/json", | |
| "x-project-id": lamaticProjectId, | |
| }, | |
| body: JSON.stringify({ | |
| query, | |
| variables: { | |
| workflowId: lamaticWorkflowId, |
| 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 : "", | ||
| }) |
There was a problem hiding this comment.
Don’t turn an invalid upstream payload into a clean review.
If executeWorkflow.result is missing or comes back in an unexpected shape, this still returns 200 with empty arrays and an empty summary. The UI will read that as “no findings” instead of surfacing the integration failure.
🚦 Fail closed on unexpected result shapes
const result = data?.data?.executeWorkflow?.result
+
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
+ return NextResponse.json(
+ { error: "Lamatic returned an unexpected workflow result shape." },
+ { status: 502 }
+ )
+ }
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 : "",
+ 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 : "",
})| "code": "let answer = \"\";\n\nif({{LLMNode_430.output.generatedResponse}}){\n answer = {{LLMNode_430.output.generatedResponse}};\n}\nelse if({{ImageGenNode_535.output.imageUrl}}){\n answer = {{ImageGenNode_535.output.imageUrl}};\n}\nelse if({{LLMNode_255.output.generatedResponse}}){\n answer = {{codeNode_904.output}};\n}\n\noutput = answer;", | ||
| "nodeName": "Finalise Output" |
There was a problem hiding this comment.
The invalid-mode branch is dropped here.
codeNode_567 feeds into this node, but answer never reads codeNode_567.output, so unsupported modes currently fall through as an empty response. Gate the JSON path on codeNode_904.output and add a final fallback for codeNode_567.output.
| typescript: { | ||
| ignoreBuildErrors: true, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Confirm local Next config behavior
rg -n "ignoreBuildErrors:\\s*true|ignoreBuildErrors:\\s*false" kits/agentic/code-review/next.config.mjs
# Check whether CI workflows explicitly run type checks
fd -HI ".*\\.ya?ml" .github/workflows | xargs -r rg -n "tsc|type.?check|next build"Repository: Lamatic/AgentKit
Length of output: 92
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if .github/workflows exists and list contents
echo "=== Checking .github/workflows ==="
if [ -d ".github/workflows" ]; then
ls -la .github/workflows
echo -e "\n=== Workflow files found ==="
find .github/workflows -type f -name "*.yml" -o -name "*.yaml"
else
echo ".github/workflows directory not found"
fi
# Check for other CI configuration files
echo -e "\n=== Checking for other CI configs ==="
fd -HI "^\.(gitlab-ci|travis|circleci)" . 2>/dev/null || echo "No gitlab-ci/travis/circleci configs found"
# Look in .github for other configuration
echo -e "\n=== Checking .github directory ==="
find .github -type f 2>/dev/null | head -20
# Check TypeScript configuration in the project
echo -e "\n=== Checking tsconfig.json in project ==="
fd -HI "tsconfig.json" | head -10Repository: Lamatic/AgentKit
Length of output: 873
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look for the actual TypeScript config for the agentic/code-review module
echo "=== Checking for TypeScript config in agentic/code-review ==="
if [ -f "kits/agentic/code-review/tsconfig.json" ]; then
echo "Found tsconfig.json:"
cat kits/agentic/code-review/tsconfig.json
fi
# Check if there's a root tsconfig
if [ -f "tsconfig.json" ]; then
echo -e "\n=== Root tsconfig.json ==="
cat tsconfig.json
fi
# Look for package.json to understand build scripts
echo -e "\n=== Checking package.json in agentic/code-review ==="
if [ -f "kits/agentic/code-review/package.json" ]; then
cat kits/agentic/code-review/package.json | jq '.scripts // {}'
fiRepository: Lamatic/AgentKit
Length of output: 898
Disable ignoreBuildErrors to prevent TypeScript errors from shipping.
The flag at line 4 bypasses Next.js build validation for TypeScript errors. With no CI enforcement of type checks for this module, disabling the flag ensures broken code cannot be built locally or deployed.
Suggested fix
const nextConfig = {
typescript: {
- ignoreBuildErrors: true,
+ ignoreBuildErrors: false,
},
images: {
unoptimized: true,
},
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| typescript: { | |
| ignoreBuildErrors: true, | |
| }, | |
| typescript: { | |
| ignoreBuildErrors: false, | |
| }, |
| "dependencies": { | ||
| "@tailwindcss/postcss": "^4.2.2", | ||
| "next": "15.1.3", | ||
| "postcss": "^8.5.8", | ||
| "react": "^19.0.0", | ||
| "react-dom": "^19.0.0", | ||
| "tailwindcss": "^4.2.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20", | ||
| "@types/react": "^19", | ||
| "@types/react-dom": "^19", | ||
| "typescript": "^5" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify unresolved bare imports against package.json deps/devDeps
python - <<'PY'
import json, re, pathlib
pkg = json.loads(pathlib.Path("kits/agentic/code-review/package.json").read_text())
declared = set(pkg.get("dependencies", {}).keys()) | set(pkg.get("devDependencies", {}).keys())
# known non-package import prefixes
skip_prefixes = ("./", "../", "@/")
# known framework/runtime packages that are declared in this manifest
# (kept generic; we'll still detect if missing)
missing = set()
for p in pathlib.Path("kits/agentic/code-review").rglob("*.tsx"):
text = p.read_text(encoding="utf-8", errors="ignore")
for m in re.finditer(r"""from\s+['"]([^'"]+)['"]""", text):
mod = m.group(1)
if mod.startswith(skip_prefixes):
continue
# normalize scoped/deep imports
base = mod.split('/')[0] if not mod.startswith('@') else '/'.join(mod.split('/')[:2])
if base not in declared and base not in {"next", "react"}:
missing.add(base)
for p in pathlib.Path("kits/agentic/code-review").rglob("*.ts"):
text = p.read_text(encoding="utf-8", errors="ignore")
for m in re.finditer(r"""from\s+['"]([^'"]+)['"]""", text):
mod = m.group(1)
if mod.startswith(skip_prefixes):
continue
base = mod.split('/')[0] if not mod.startswith('@') else '/'.join(mod.split('/')[:2])
if base not in declared and base not in {"next", "react"}:
missing.add(base)
print("Missing packages:", sorted(missing))
PYRepository: Lamatic/AgentKit
Length of output: 1082
🏁 Script executed:
#!/bin/bash
# Check for actual imports of the detected missing packages
echo "=== Checking for `@radix-ui/react-label` usage ==="
rg "@radix-ui/react-label" kits/agentic/code-review/ -A 2
echo ""
echo "=== Checking for lucide-react usage ==="
rg "lucide-react" kits/agentic/code-review/ -A 2
echo ""
echo "=== Checking for clsx usage ==="
rg "clsx" kits/agentic/code-review/ -A 2 | head -20
echo ""
echo "=== Sample of other major missing packages ==="
rg "(react-hook-form|recharts|sonner)" kits/agentic/code-review/ -A 2 | head -30Repository: Lamatic/AgentKit
Length of output: 7745
Add comprehensive missing dependencies to package.json.
Multiple required packages are imported in UI components and utilities but not declared: @radix-ui/* (react-label, react-slot, and others), lucide-react, clsx, tailwind-merge, react-hook-form, recharts, sonner, react-day-picker, react-resizable-panels, embla-carousel-react, input-otp, cmdk, class-variance-authority, next-themes, vaul, and lamatic. These are actively used across the component library and will cause build/install failures.
| # Agent Kit Generation by Lamatic.ai | ||
|
|
||
| <p align="center"> | ||
| <a href="https://agent-kit-generation.vercel.app" target="_blank"> | ||
| <img src="https://img.shields.io/badge/Live%20Demo-black?style=for-the-badge" alt="Live Demo" /> | ||
| </a> | ||
| </p> | ||
|
|
||
|
|
||
| **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. | ||
|
|
||
| [](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) |
There was a problem hiding this comment.
The README is still for the generation kit.
Lines 1-12, 24, and 81-83 refer to "Agent Kit Generation", the Generation template, and a generation form/deploy target. Anyone following this README will provision or navigate to the wrong kit.
Also applies to: 24-24, 81-83
| "compilerOptions": { | ||
| "target": "ES2017", | ||
| "lib": [ | ||
| "dom", | ||
| "dom.iterable", | ||
| "esnext" | ||
| ], | ||
| "allowJs": true, | ||
| "skipLibCheck": true, | ||
| "strict": false, | ||
| "noEmit": true, | ||
| "incremental": true, | ||
| "module": "esnext", | ||
| "esModuleInterop": true, | ||
| "moduleResolution": "node", | ||
| "resolveJsonModule": true, | ||
| "isolatedModules": true, | ||
| "jsx": "preserve", | ||
| "plugins": [ | ||
| { | ||
| "name": "next" | ||
| } | ||
| ] | ||
| }, | ||
| "include": [ | ||
| "next-env.d.ts", | ||
| ".next/types/**/*.ts", | ||
| "**/*.ts", | ||
| "**/*.tsx" | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Alias imports using `@/` =="
rg -n "from ['\"]@/" kits/agentic/code-review --type ts --type tsx
echo
echo "== baseUrl/paths presence in tsconfig =="
rg -n '"baseUrl"|"paths"' kits/agentic/code-review/tsconfig.jsonRepository: Lamatic/AgentKit
Length of output: 133
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Searching for `@/` alias imports =="
find kits/agentic/code-review -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -exec rg "from ['\"]@/" {} + 2>/dev/null || echo "(no `@/` imports found or rg error)"
echo
echo "== Full tsconfig.json content =="
cat kits/agentic/code-review/tsconfig.json
echo
echo "== Searching for baseUrl or paths in tsconfig =="
rg -n "baseUrl|paths" kits/agentic/code-review/tsconfig.json || echo "(baseUrl/paths not found)"Repository: Lamatic/AgentKit
Length of output: 7773
Add baseUrl and paths to tsconfig compilerOptions to resolve @/ imports.
The codebase extensively uses @/ aliases across 68+ files (@/lib/utils, @/components/ui/*, @/hooks/*, etc.), but compilerOptions lacks the required baseUrl and paths configuration. This will cause TypeScript import resolution to fail.
Required tsconfig fix
"compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./*"]
+ },
"target": "ES2017",📝 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.
| "compilerOptions": { | |
| "target": "ES2017", | |
| "lib": [ | |
| "dom", | |
| "dom.iterable", | |
| "esnext" | |
| ], | |
| "allowJs": true, | |
| "skipLibCheck": true, | |
| "strict": false, | |
| "noEmit": true, | |
| "incremental": true, | |
| "module": "esnext", | |
| "esModuleInterop": true, | |
| "moduleResolution": "node", | |
| "resolveJsonModule": true, | |
| "isolatedModules": true, | |
| "jsx": "preserve", | |
| "plugins": [ | |
| { | |
| "name": "next" | |
| } | |
| ] | |
| }, | |
| "include": [ | |
| "next-env.d.ts", | |
| ".next/types/**/*.ts", | |
| "**/*.ts", | |
| "**/*.tsx" | |
| ], | |
| "compilerOptions": { | |
| "baseUrl": ".", | |
| "paths": { | |
| "@/*": ["./*"] | |
| }, | |
| "target": "ES2017", | |
| "lib": [ | |
| "dom", | |
| "dom.iterable", | |
| "esnext" | |
| ], | |
| "allowJs": true, | |
| "skipLibCheck": true, | |
| "strict": false, | |
| "noEmit": true, | |
| "incremental": true, | |
| "module": "esnext", | |
| "esModuleInterop": true, | |
| "moduleResolution": "node", | |
| "resolveJsonModule": true, | |
| "isolatedModules": true, | |
| "jsx": "preserve", | |
| "plugins": [ | |
| { | |
| "name": "next" | |
| } | |
| ] | |
| }, | |
| "include": [ | |
| "next-env.d.ts", | |
| ".next/types/**/*.ts", | |
| "**/*.ts", | |
| "**/*.tsx" | |
| ], |
Code Review Agent Kit
Overview
code-review-agentAgentKit - a complete Next.js application for AI-powered GitHub PR code reviews using Lamatic.aiKey Components
owner,repo, andpr_numberinputs/api/review) that executes Lamatic workflows to analyze code and return findings categorized by bugs, security issues, and style suggestionsgenerateContent()function for orchestrating Lamatic workflow execution with flexible input types (text, image, json)Workflows & Configuration
agentic-generate-contentwith 9-node workflow supporting text generation, image generation, and JSON output modesStyling & Infrastructure
Documentation & Setup
Status: Initial feature implementation - 2,600+ lines of code across ~130 new files