Skip to content

feat: Add mock-ai interviewer Agentkit#101

Open
sreecharan1306 wants to merge 9 commits into
Lamatic:old-mainfrom
sreecharan1306:main
Open

feat: Add mock-ai interviewer Agentkit#101
sreecharan1306 wants to merge 9 commits into
Lamatic:old-mainfrom
sreecharan1306:main

Conversation

@sreecharan1306
Copy link
Copy Markdown

@sreecharan1306 sreecharan1306 commented Mar 24, 2026

What This Kit Does

Mockai is an AI-powered mock interviewer that helps users practice for tailored job interviews. It conducts dynamic, interactive interviews using real-time speech-to-text and provides instant, AI-generated feedback on your performance.

Providers & Prerequisites

  • External Providers: Google Gemini
  • Prerequisites: No special setup required

How to Run Locally

  1. cd kits/agentic/mockai
  2. npm install
  3. cp .env.example .env and fill in values
  4. npm run dev

Live Preview

https://mockai-mscr.vercel.app/

Lamatic Flow

  • Question Flow ID: 9f64b62e-6e02-4de5-afa9-677970913e00
  • Feedback Flow ID: 7a20f813-2e7a-4597-804f-427b9b702d88

Mockai - AI-Powered Mock Interviewer Agent Kit (PR #101)

Overview

Adds a complete, production-ready Next.js-based AI mock interview platform with three-step interactive workflow (setup → interview → feedback). Integrates Google Gemini via Lamatic.ai orchestration for dynamic question generation and real-time interview evaluation with speech-to-text capabilities.


Files Added: 102 Total

Configuration & Build Setup (16 files)

  • .env.example - LAMATIC API credentials and flow IDs
  • .gitignore - Node/Next.js standard exclusions
  • package.json - Dependencies (Next.js, React, Tailwind CSS, Radix UI, Recharts, Sonner, date-fns, input-otp, embla-carousel)
  • package-lock.json - Locked dependency versions
  • next.config.mjs - Next.js config (standalone output, image optimization disabled)
  • tsconfig.json - TypeScript strict mode with Next.js plugin
  • postcss.config.mjs - PostCSS for Tailwind CSS
  • components.json - Shadcn UI schema configuration
  • config.json - Agent kit metadata and orchestration config
  • Flow configuration files (3 flows × 4 files: config.json, inputs.json, meta.json, README.md)

Core Application (3 files)

  • app/layout.tsx - Root layout with global CSS, Google Fonts (Geist), Vercel Analytics
  • app/page.tsx - Main interview component (~579 lines) with Web Speech API integration, audio visualizer, 3-step workflow management
  • app/globals.css - Global styles with Tailwind v4, CSS variable theming (light/dark modes), chart colors, sidebar variants

Server Logic & Utilities (5 files)

  • actions/orchestrate.ts - Server actions: generateQuestions() and evaluateAnswers() with Lamatic flow execution and error handling
  • lib/lamatic-client.ts - Lamatic SDK instantiation with environment credentials
  • lib/utils.ts - cn() utility for class merging (clsx + tailwind-merge)
  • orchestrate.js - Orchestration config with flows and API settings
  • hooks/use-toast.ts - Toast state management with dispatch/subscribe pattern

UI Components (59 files)

  • Layout: header.tsx, theme-provider.tsx, sidebar.tsx (with collapsible variants: offcanvas/icon/none)
  • Forms: form.tsx, input.tsx, textarea.tsx, label.tsx, checkbox.tsx, radio-group.tsx, switch.tsx, field.tsx, input-group.tsx, input-otp.tsx, select.tsx
  • Dialogs/Overlays: alert-dialog.tsx, dialog.tsx, drawer.tsx, sheet.tsx, popover.tsx, hover-card.tsx, context-menu.tsx
  • Data Display: table.tsx, card.tsx, badge.tsx, avatar.tsx, skeleton.tsx, pagination.tsx, breadcrumb.tsx, item.tsx, empty.tsx, carousel.tsx, calendar.tsx
  • Navigation: tabs.tsx, accordion.tsx, collapsible.tsx, menubar.tsx, navigation-menu.tsx, dropdown-menu.tsx, command.tsx
  • Buttons/Toggles: button.tsx, button-group.tsx, toggle.tsx, toggle-group.tsx
  • Interactive: slider.tsx, progress.tsx, scroll-area.tsx, resizable.tsx, chart.tsx
  • Utilities: tooltip.tsx, alert.tsx, kbd.tsx, separator.tsx, spinner.tsx, sonner.tsx (toast), toaster.tsx, use-mobile.tsx
  • Additional: AspectRatio.tsx, use-mobile.tsx (duplicate hook)

Hooks (2 files)

  • hooks/use-mobile.ts - Responsive mobile detection hook (768px breakpoint)
  • hooks/use-toast.ts - Toast state management with in-memory store, unique IDs, auto-dismissal timers

Flows (12 files - 3 flows × 4 files)

1. Question Flow (flows/question-flow/)

  • Node Types: triggerNode → InstructorLLMNode → responseNode
  • Trigger Input: { jobTitle: string, yearsOfExp: int, jobDesc: string }
  • Processing: InstructorLLMNode generates structured JSON with questions array (10 interview questions)
  • Output: { questions: string[] }
  • Prompt: "You are a highly experienced interviewer... prepare a list of 10 questions..."

2. Feedback Flow (flows/feedback-flow/)

  • Node Types: triggerNode → InstructorLLMNode → responseNode
  • Trigger Input: { candidateResponses: [{ question: string, answers: string }] }
  • Processing: InstructorLLMNode evaluates answers and generates structured JSON
  • Output Schema: { positives: string[], negatives: string[], rating: number }
  • Prompt: "You are an expert interview judge... give positives, negatives, and score 1-10... minimum 1 point, maximum 5 points"

3. Generate Content Flow (flows/agentic-generate-content/) - Optional/reusable

  • Node Types: triggerNode → conditionNode → (3 parallel LLM paths) → code nodes → responseNode
  • Trigger Input: { mode: "text" | "image" | "json", instructions: string }
  • Conditional Branching:
    • mode == "text": routes to LLMNode_430 (text generation)
    • mode == "image": routes to ImageGenNode_535 (image generation)
    • mode == "json": routes to LLMNode_255 (JSON generation)
    • else: invalid mode error code node
  • Finalization: All paths converge at codeNode_136 (Finalise Output) before API response

Assets & Documentation (11 files)

  • README.md - Setup instructions, environment variables, repository structure, contributing guidelines
  • PR_DESCRIPTION.md - PR-specific test instructions and feature highlights
  • Image assets: placeholder.jpg, placeholder.svg, placeholder-user.jpg, lamatic-logo.png, icon.svg, icon-light-32x32.png, icon-dark-32x32.png, apple-icon.png, placeholder-logo.svg, placeholder-logo.png

Interview Workflow (Client-Side)

  1. Setup Step

    • Collects: job title, years of experience, optional job description
    • Validates required fields
    • Calls generateQuestions() → Question Flow
    • Transitions to interview step on success
  2. Interview Step

    • Displays 1 question at a time
    • Answer capture via textarea + Web Speech API:
      • Real-time transcription with interim transcript display
      • Audio visualizer (Web Audio API)
      • Auto-clamped to MAX_CHARS limit
      • Error handling for microphone permissions/speech API unsupported
    • Navigation: "Next Question" button
      • Finalizes answer, enforces non-empty responses
      • Stores response to history
      • Either advances to next question OR calls evaluateAnswers() on final question
  3. Feedback Step

    • Displays AI-generated feedback:
      • Numeric rating with color-coding (score-based)
      • Strengths (positives)
      • Improvement areas (negatives)
    • Reset action clears all state, stops recording

Technology Stack

Category Libraries
Framework Next.js 15+, React 19
Styling Tailwind CSS v4, CSS Variables, PostCSS
UI Components Radix UI, Shadcn-style wrappers
Forms React Hook Form, Zod
Speech Web Speech API (browser native)
Audio Web Audio API (visualizer)
AI/Orchestration Lamatic.ai, Google Gemini
Notifications Sonner (toast)
Charts Recharts
Icons Lucide React
Utilities date-fns, clsx, tailwind-merge
Analytics Vercel Analytics

Key Features Implemented

✅ Real-time speech-to-text with Web Speech API
✅ Audio visualizer during recording
✅ Three-step interactive interview workflow
✅ AI-generated questions from Lamatic flows
✅ Structured feedback (positives/negatives/rating)
✅ Dark/light theme with CSS variables
✅ Responsive design (mobile-aware sidebar)
✅ Toast notifications (Sonner)
✅ Comprehensive UI component library (59 components)
✅ Environment-driven flow configuration
✅ Production-ready Next.js setup (standalone mode, TypeScript strict)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 24, 2026

Code Review Briefing

Walkthrough

Your mission, should you choose to accept it: a new agentic AI interview prep kit called "Mockai" is being deployed. The implementation integrates Lamatic.ai orchestration with a Next.js frontend, providing a three-step mock interview workflow (setup, interview with live speech-to-text, feedback) backed by configurable AI flows and a comprehensive shadcn UI component library. This pull request is self-contained and ready for deployment.

Changes

Mockai Interview Prep Kit

Layer / File(s) Summary
Project Setup & Dependencies
package.json, tsconfig.json, next.config.mjs, postcss.config.mjs, .env.example, .gitignore
Next.js/React project initialized with Tailwind, form validation, Radix UI, and Lamatic.ai integration. TypeScript strict mode, standalone output, and image optimization disabled. Dependencies include sonner, recharts, date-fns, and speech recognition support via browser APIs.
Styling & Theme System
app/globals.css, styles/globals.css, app/layout.tsx, components/theme-provider.tsx
Dual CSS globals define Tailwind theme tokens (light/dark mode), root layout applies Google fonts (Geist), Vercel Analytics, and theme provider wraps the app for next-themes integration.
Utilities & Client Configuration
lib/utils.ts, lib/lamatic-client.ts, orchestrate.js
cn() utility merges classnames via tailwind-merge. Lamatic client instantiated from environment credentials. Orchestration config defines question and feedback flows with environment-variable-bound workflow IDs.
UI Component Library
components/ui/*.tsx
60+ shadcn/Radix UI wrapper components covering buttons, inputs, dialogs, dropdowns, menus, forms, tables, charts, carousels, toasts, tooltips, sidebars, and layout primitives. Each component applies consistent data-slot attributes and Tailwind styling.
State Management & Custom Hooks
hooks/use-toast.ts, hooks/use-mobile.ts, components/ui/use-toast.ts, components/ui/use-mobile.ts
In-memory toast store with action-driven state, removal queue, and React hook subscription. Mobile breakpoint detection via matchMedia with listener cleanup.
Header & Layout Components
components/header.tsx
Sticky header with gradient icon, "Mockai" branding, and "Interview Prep Area" label.
Server-Side Orchestration
actions/orchestrate.ts
Two server actions: generateQuestions validates job inputs, executes Lamatic question flow, returns parsed questions array; evaluateAnswers validates responses, executes feedback flow, returns structured { positives, negatives, rating }. Both include error mapping for network/auth failures.
Main Interview Page
app/page.tsx
Three-stage interview workflow: setup collects job title/experience/description; interview manages question state, live speech-to-text via Web Speech API with audio visualizer, per-question answer recording; feedback renders rating with color-coding and improvement areas. Includes microphone permission handling, recording cleanup on unmount.
Flow Definitions & Schemas
config.json, orchestrate.js, flows/*/config.json, flows/*/inputs.json, flows/*/meta.json, flows/*/README.md
Agent kit metadata, two Lamatic flows (question generation and feedback evaluation), each with node graph configs, model selection inputs, metadata, and documentation.
Documentation
README.md, PR_DESCRIPTION.md
Setup instructions, environment variables, repository structure overview, and testing guidance.

Suggested reviewers

  • amanintech
  • d-pamneja

This message will self-destruct in five seconds. Your task is to review the implementation for correctness, security of credential handling, Web Speech API compatibility, and integration completeness with Lamatic flows. Good luck.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@sreecharan1306 sreecharan1306 marked this pull request as ready for review March 25, 2026 14:30
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Note

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

🟡 Minor comments (17)
kits/agentic/mockai/.gitignore-19-21 (1)

19-21: ⚠️ Potential issue | 🟡 Minor

Add .env.local to prevent accidental secret commits.

The .gitignore excludes .env but is missing .env.local, which is commonly used by Next.js for local environment variables. As per coding guidelines, both .env and .env.local must be excluded.

Proposed fix
 # env files
 .env
+.env.local
kits/agentic/mockai/components/ui/slider.tsx-16-24 (1)

16-24: ⚠️ Potential issue | 🟡 Minor

Reconsider the [min, max] fallback behavior for _values.

When neither value nor defaultValue is provided, _values defaults to [min, max], which renders exactly two thumbs regardless of intended use. This creates a range slider by default, which may not match the expected behavior for a single-value slider.

Consider using [min] as the fallback to render a single thumb by default, or document that consumers must provide an explicit defaultValue.

kits/agentic/mockai/components/ui/kbd.tsx-18-24 (1)

18-24: ⚠️ Potential issue | 🟡 Minor

Use a structural wrapper for KbdGroup instead of <kbd>.

KbdGroup is typed as React.ComponentProps<'div'> but currently renders <kbd>, which gives incorrect semantics for a group container.

Proposed fix
 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/mockai/components/ui/carousel.tsx-64-105 (1)

64-105: ⚠️ Potential issue | 🟡 Minor

Add symmetric cleanup for the reInit event listener in the effect's return statement.

The code subscribes to both reInit and select events (lines 99–100), but the cleanup only unsubscribes from select (line 105). Since Embla listeners persist until explicitly removed via off(), the reInit listener should also be cleaned up to prevent memory leaks.

Current cleanup (incomplete)
return () => {
  api?.off('select', onSelect)
}

Update to:

return () => {
  api?.off('reInit', onSelect)
  api?.off('select', onSelect)
}
kits/agentic/mockai/components/ui/carousel.tsx-78-88 (1)

78-88: ⚠️ Potential issue | 🟡 Minor

Match keyboard navigation to the chosen orientation.

Lines 80-85 always consume Left/Right. When orientation="vertical", the buttons render as up/down controls but the keyboard path still ignores ArrowUp/ArrowDown, so vertical carousels are inconsistent for keyboard users.

♿ Proposed fix
   const handleKeyDown = React.useCallback(
     (event: React.KeyboardEvent<HTMLDivElement>) => {
-      if (event.key === 'ArrowLeft') {
+      const prevKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp'
+      const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown'
+
+      if (event.key === prevKey) {
         event.preventDefault()
         scrollPrev()
-      } else if (event.key === 'ArrowRight') {
+      } else if (event.key === nextKey) {
         event.preventDefault()
         scrollNext()
       }
     },
-    [scrollPrev, scrollNext],
+    [orientation, scrollPrev, scrollNext],
   )
kits/agentic/mockai/hooks/use-toast.ts-174-182 (1)

174-182: ⚠️ Potential issue | 🟡 Minor

Incorrect useEffect dependency causes unnecessary re-subscriptions.

The dependency array includes [state], but the effect only subscribes/unsubscribes the listener. This causes the effect to re-run on every state change, repeatedly adding and removing the same setState from the listeners array. The dependency should be empty [] to subscribe once on mount and cleanup on unmount.

🐛 Proposed fix
   React.useEffect(() => {
     listeners.push(setState)
     return () => {
       const index = listeners.indexOf(setState)
       if (index > -1) {
         listeners.splice(index, 1)
       }
     }
-  }, [state])
+  }, [])
kits/agentic/mockai/components/ui/empty.tsx-71-81 (1)

71-81: ⚠️ Potential issue | 🟡 Minor

Type and element mismatch in EmptyDescription.

The component is typed as React.ComponentProps<'p'> but renders a <div> element. This inconsistency could cause confusion when consumers expect paragraph-specific behavior or accessibility semantics.

🐛 Proposed fix - align type with element
-function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
+function EmptyDescription({ className, ...props }: React.ComponentProps<'div'>) {
   return (
     <div
       data-slot="empty-description"

Or alternatively, render a <p> element to match 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}
-    />
+    />
   )
 }
kits/agentic/mockai/components/ui/input-group.tsx-70-76 (1)

70-76: ⚠️ Potential issue | 🟡 Minor

Addon click handler can be silently overridden and misses textarea focus.

Because ...props is spread after onClick, consumer onClick can replace this logic entirely. Also, focusing only input skips InputGroupTextarea controls.

💡 Proposed fix
 function InputGroupAddon({
   className,
   align = 'inline-start',
+  onClick,
   ...props
 }: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
   return (
     <div
@@
-      onClick={(e) => {
-        if ((e.target as HTMLElement).closest('button')) {
+      onClick={(e) => {
+        onClick?.(e)
+        if (e.defaultPrevented) return
+        if ((e.target as HTMLElement).closest('button,[role="button"]')) {
           return
         }
-        e.currentTarget.parentElement?.querySelector('input')?.focus()
+        e.currentTarget.parentElement
+          ?.querySelector<HTMLElement>('[data-slot="input-group-control"]')
+          ?.focus()
       }}
       {...props}
     />
   )
 }
kits/agentic/mockai/components/ui/field.tsx-194-229 (1)

194-229: ⚠️ Potential issue | 🟡 Minor

Don't render an empty alert when no error messages exist.

errors=[] (or entries without message) still produces a truthy empty <ul>, so FieldError renders role="alert" with no content. Filter the messages first and return null when the filtered list is empty.

Suggested fix
   const content = useMemo(() => {
     if (children) {
       return children
     }
 
-    if (!errors) {
+    const messages =
+      errors?.flatMap((error) => (error?.message ? [error.message] : [])) ?? []
+
+    if (messages.length === 0) {
       return null
     }
 
-    if (errors.length === 1 && errors[0]?.message) {
-      return errors[0].message
+    if (messages.length === 1) {
+      return messages[0]
     }
 
     return (
       <ul className="ml-4 flex list-disc flex-col gap-1">
-        {errors.map(
-          (error, index) =>
-            error?.message && <li key={index}>{error.message}</li>,
-        )}
+        {messages.map((message, index) => (
+          <li key={index}>{message}</li>
+        ))}
       </ul>
     )
   }, [children, errors])
kits/agentic/mockai/components/ui/sidebar.tsx-609-612 (1)

609-612: ⚠️ Potential issue | 🟡 Minor

Remove or make Math.random() deterministic.

While this component is marked 'use client' and doesn't face hydration mismatches, Math.random() in the render path still creates non-deterministic skeleton widths on each mount, which can complicate testing and reproducibility. Consider accepting the width as a prop, using a seeded random number, or applying a fixed width instead.

kits/agentic/mockai/orchestrate.js-1-44 (1)

1-44: ⚠️ Potential issue | 🟡 Minor

Convert to TypeScript and fix type issues.

This file should be orchestrate.ts per the project guideline requiring TypeScript for all kit files. Additionally:

  1. polling should be boolean: Lines 19 and 36 use "false" (string) instead of false (boolean).
  2. Missing type definitions: TypeScript would provide better compile-time safety for the config structure.
🔧 Suggested TypeScript conversion
-export const config = {
+interface FlowConfig {
+  name: string;
+  type: string;
+  workflowId: string | undefined;
+  description: string;
+  expectedOutput: string[];
+  inputSchema: Record<string, string>;
+  outputSchema: Record<string, string>;
+  mode: string;
+  polling: boolean;
+}
+
+interface OrchestrateConfig {
+  type: string;
+  flows: Record<string, FlowConfig>;
+  api: {
+    endpoint: string | undefined;
+    projectId: string | undefined;
+    apiKey: string | undefined;
+  };
+}
+
+export const config: OrchestrateConfig = {
     "type": "atomic",
     "flows": {
       "question" : {
           ...
           "mode": "sync",
-          "polling" : "false"
+          "polling" : false
       },
       "feedback" : {
           ...
-          "polling" : "false"
+          "polling" : false
       }
     },
     ...
 }

As per coding guidelines: "Use TypeScript for all kit components and server actions".

kits/agentic/mockai/app/layout.tsx-6-7 (1)

6-7: ⚠️ Potential issue | 🟡 Minor

Loaded fonts are not applied to the DOM.

The Geist font objects are instantiated but their class names are never applied. The underscore prefix suggests these are intentionally unused, but this means the fonts won't actually render.

🛠️ Proposed fix to apply the fonts
-const _geist = Geist({ subsets: ["latin"] });
-const _geistMono = Geist_Mono({ subsets: ["latin"] });
+const geist = Geist({ subsets: ["latin"], variable: "--font-geist-sans" });
+const geistMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" });

Then apply to the body:

-      <body className={`font-sans antialiased`}>
+      <body className={`${geist.variable} ${geistMono.variable} font-sans antialiased`}>
kits/agentic/mockai/.env.example-1-6 (1)

1-6: ⚠️ Potential issue | 🟡 Minor

Fix inconsistent .env formatting.

The file has inconsistent spacing around = signs (lines 2-4 have spaces, lines 5-6 don't) and is missing a trailing newline. Some env parsers may behave unexpectedly with spaces around =.

🛠️ Proposed fix for consistent formatting
-# 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_FEEDBACK_FLOW_ID="Feedback agent Flow ID"
-AGENTIC_QUESTION_FLOW_ID="Question agent Flow ID"
+# AGENTIC_GENERATE_CONTENT="AGENTIC_GENERATE_CONTENT Flow ID"
+LAMATIC_API_URL="your_lamatic_api_url"
+LAMATIC_PROJECT_ID="your_lamatic_project_id"
+LAMATIC_API_KEY="your_lamatic_api_key"
+AGENTIC_FEEDBACK_FLOW_ID="your_feedback_flow_id"
+AGENTIC_QUESTION_FLOW_ID="your_question_flow_id"
kits/agentic/mockai/README.md-2-4 (1)

2-4: ⚠️ Potential issue | 🟡 Minor

Add alt text to the hero image.

The <img> tag is missing alt, which is already being flagged by markdownlint and hurts accessibility for screen-reader users.

kits/agentic/mockai/README.md-82-97 (1)

82-97: ⚠️ Potential issue | 🟡 Minor

Add a language to the repo-structure fence.

The block starting on Line 82 is unlabeled, so it will keep tripping MD040. text is enough here.

kits/agentic/mockai/components/ui/chart.tsx-235-239 (1)

235-239: ⚠️ Potential issue | 🟡 Minor

Render 0 values in the tooltip.

The current truthy check hides legitimate zero datapoints, so a series value of 0 disappears from the tooltip. Check for null/undefined instead.

🩹 Minimal 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/mockai/components/ui/toast.tsx-77-87 (1)

77-87: ⚠️ Potential issue | 🟡 Minor

Give the close control an accessible name.

This is currently an icon-only button, so assistive tech will announce an unnamed control. Add aria-label or hidden text.

♿ Minimal fix
   <ToastPrimitives.Close
     ref={ref}
+    aria-label="Close toast"
     className={cn(
       'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
       className,
     )}
🧹 Nitpick comments (26)
kits/agentic/mockai/components/ui/use-mobile.tsx (1)

10-12: Prefer event.matches over rechecking window.innerWidth.

The change event already provides the query result via event.matches. Using it avoids redundant property access and is more idiomatic.

♻️ Suggested improvement
-    const onChange = () => {
-      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
-    }
+    const onChange = (event: MediaQueryListEvent) => {
+      setIsMobile(event.matches)
+    }
kits/agentic/mockai/components/ui/hover-card.tsx (1)

1-1: Rename this component file to PascalCase.

The file path uses hover-card.tsx; please rename to HoverCard.tsx to match the components filename convention.

As per coding guidelines: kits/**/components/**/*.{tsx,ts}: Use PascalCase for React component filenames in the components/ directory.

kits/agentic/mockai/components/ui/collapsible.tsx (1)

1-33: Filename casing does not follow the component naming guideline.

kits/agentic/mockai/components/ui/collapsible.tsx is lowercase; guideline requires PascalCase filenames in components/.

Rename to Collapsible.tsx (and update imports accordingly).
Based on learnings, "Applies to kits//components//*.{tsx,ts} : Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/skeleton.tsx (1)

1-13: Consider PascalCase filename to match coding guidelines.

The file is named skeleton.tsx but the coding guidelines specify PascalCase for React component filenames in the components/ directory (e.g., Skeleton.tsx). However, this follows standard shadcn/ui naming conventions which use lowercase. Consider whether to align with project guidelines or maintain shadcn/ui consistency across all UI components.

kits/agentic/mockai/hooks/use-mobile.ts (1)

5-19: Consider exposing loading state to prevent layout flash on mobile.

The hook coerces undefined to false via !!isMobile, meaning mobile users briefly see desktop layout until the effect runs. For layout-critical usage, consider returning undefined during the indeterminate state so consumers can show a loading placeholder or skeleton.

Alternative signature if needed
-export function useIsMobile() {
+export function useIsMobile(): boolean | undefined {
   const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
   // ... effect unchanged ...
-  return !!isMobile
+  return isMobile
 }
kits/agentic/mockai/components/ui/switch.tsx (1)

1-31: Consider renaming to Switch.tsx for consistency with coding guidelines.

The component implementation correctly wraps @radix-ui/react-switch with proper props forwarding, data-slot attributes, and Tailwind styling. However, the filename uses lowercase (switch.tsx) instead of PascalCase (Switch.tsx).

As per coding guidelines: "Use PascalCase for React component filenames in the components/ directory."

kits/agentic/mockai/components/ui/kbd.tsx (1)

1-1: Rename lowercase component files in components/ui to PascalCase.

This PR introduces lowercase component filenames (kbd.tsx, resizable.tsx, button.tsx, alert.tsx, calendar.tsx, scroll-area.tsx). Please rename them to PascalCase to align with kit conventions.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/tsconfig.json (1)

37-38: Remove duplicated include glob entry.

".next\\dev/types/**/*.ts" is listed twice; dropping the duplicate keeps the config cleaner.

Proposed fix
   "include": [
     "next-env.d.ts",
     "**/*.ts",
     "**/*.tsx",
     ".next/types/**/*.ts",
     ".next/dev/types/**/*.ts",
-    ".next\\dev/types/**/*.ts",
     ".next\\dev/types/**/*.ts"
   ],
kits/agentic/mockai/components/ui/toggle-group.tsx (1)

1-73: Rename the new UI component files to PascalCase.

This file—and the other newly added components/ui/*.tsx wrappers in this PR—uses lowercase/kebab-case filenames, which diverges from the repo convention for React component files and will keep imports inconsistent across kits.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/pagination.tsx (1)

9-9: Unused import Button.

Only buttonVariants is used in this file; Button is imported but never referenced.

♻️ Proposed fix
-import { Button, buttonVariants } from '@/components/ui/button'
+import { buttonVariants } from '@/components/ui/button'
kits/agentic/mockai/components/ui/avatar.tsx (1)

1-53: Rename this file to PascalCase.

Please rename avatar.tsx to Avatar.tsx to match the kit component filename convention.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/sheet.tsx (1)

1-139: Rename this file to PascalCase.

Please rename sheet.tsx to Sheet.tsx to align with the component filename rule.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/alert-dialog.tsx (1)

1-157: Rename this file to PascalCase.

Please rename alert-dialog.tsx to AlertDialog.tsx for consistency with kit component naming standards.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/card.tsx (1)

1-92: Rename this file to PascalCase.

Please rename card.tsx to Card.tsx to satisfy the components directory naming convention.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/input-group.tsx (1)

1-169: Rename this file to PascalCase.

Please rename input-group.tsx to InputGroup.tsx to comply with the kit naming convention.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/breadcrumb.tsx (1)

1-109: Rename this file to PascalCase.

Please rename breadcrumb.tsx to Breadcrumb.tsx to follow the required components naming style.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/navigation-menu.tsx (1)

1-166: Rename this file to PascalCase.

Please rename navigation-menu.tsx to NavigationMenu.tsx to match the enforced naming convention.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/drawer.tsx (1)

1-135: Rename this file to PascalCase.

Please rename drawer.tsx to Drawer.tsx to adhere to the component filename rule in kits.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/ui/context-menu.tsx (1)

1-252: Rename these new UI primitive files to PascalCase.

This file is being added under components/ as context-menu.tsx, and the same kebab-case pattern shows up in the sibling UI primitives in this PR. That diverges from the kit naming convention and will make imports inconsistent.

As per coding guidelines, "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/components/theme-provider.tsx (1)

1-11: Rename file to PascalCase.

The filename theme-provider.tsx should be ThemeProvider.tsx to comply with the project's component naming conventions. As per coding guidelines: "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/flows/feedback-flow/meta.json (1)

1-9: Consider adding a description for discoverability.

The description and tags fields are empty. Adding a brief description (e.g., "Evaluates candidate interview responses and provides structured feedback with ratings") would improve documentation and flow discoverability.

kits/agentic/mockai/components/header.tsx (1)

1-24: Rename file to PascalCase.

The filename header.tsx should be Header.tsx to comply with the project's component naming conventions. The component implementation itself is clean and correctly uses lucide-react for icons. As per coding guidelines: "Use PascalCase for React component filenames in the components/ directory".

kits/agentic/mockai/flows/agentic-generate-content/meta.json (1)

1-12: Flow metadata seems misaligned with mockai kit purpose.

This flow is named "Agentic Generation - Generate Content" with a test input for "write me a poem on AI", but the mockai kit is an interview preparation tool. Consider renaming to reflect the actual interview question generation use case, and adding a relevant description.

kits/agentic/mockai/flows/question-flow/config.json (1)

44-46: Remove the scaffolded user prompt.

Line 46 still sends Write your prompt here to the model. That placeholder becomes part of the real request, so either replace it with the interview-generation prompt or drop the extra user turn.

kits/agentic/mockai/components/ui/toast.tsx (1)

1-1: Rename this component file to PascalCase.

toast.tsx is a React component module under components/, so the filename misses the kit convention.

As per coding guidelines, Use PascalCase for React component filenames in the components/ directory.

kits/agentic/mockai/components/ui/chart.tsx (1)

1-1: Rename this component file to PascalCase.

chart.tsx is a React component module under components/, so the filename misses the kit convention.

As per coding guidelines, Use PascalCase for React component filenames in the components/ directory.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2a95e0f-8071-4117-862c-474c3e77a616

📥 Commits

Reviewing files that changed from the base of the PR and between cd0d519 and a92235c.

⛔ Files ignored due to path filters (11)
  • kits/agentic/mockai/package-lock.json is excluded by !**/package-lock.json
  • kits/agentic/mockai/public/apple-icon.png is excluded by !**/*.png
  • kits/agentic/mockai/public/icon-dark-32x32.png is excluded by !**/*.png
  • kits/agentic/mockai/public/icon-light-32x32.png is excluded by !**/*.png
  • kits/agentic/mockai/public/icon.svg is excluded by !**/*.svg
  • kits/agentic/mockai/public/lamatic-logo.png is excluded by !**/*.png
  • kits/agentic/mockai/public/placeholder-logo.png is excluded by !**/*.png
  • kits/agentic/mockai/public/placeholder-logo.svg is excluded by !**/*.svg
  • kits/agentic/mockai/public/placeholder-user.jpg is excluded by !**/*.jpg
  • kits/agentic/mockai/public/placeholder.jpg is excluded by !**/*.jpg
  • kits/agentic/mockai/public/placeholder.svg is excluded by !**/*.svg
📒 Files selected for processing (90)
  • kits/agentic/mockai/.env.example
  • kits/agentic/mockai/.gitignore
  • kits/agentic/mockai/README.md
  • kits/agentic/mockai/actions/orchestrate.ts
  • kits/agentic/mockai/app/globals.css
  • kits/agentic/mockai/app/layout.tsx
  • kits/agentic/mockai/app/page.tsx
  • kits/agentic/mockai/components.json
  • kits/agentic/mockai/components/header.tsx
  • kits/agentic/mockai/components/theme-provider.tsx
  • kits/agentic/mockai/components/ui/accordion.tsx
  • kits/agentic/mockai/components/ui/alert-dialog.tsx
  • kits/agentic/mockai/components/ui/alert.tsx
  • kits/agentic/mockai/components/ui/aspect-ratio.tsx
  • kits/agentic/mockai/components/ui/avatar.tsx
  • kits/agentic/mockai/components/ui/badge.tsx
  • kits/agentic/mockai/components/ui/breadcrumb.tsx
  • kits/agentic/mockai/components/ui/button-group.tsx
  • kits/agentic/mockai/components/ui/button.tsx
  • kits/agentic/mockai/components/ui/calendar.tsx
  • kits/agentic/mockai/components/ui/card.tsx
  • kits/agentic/mockai/components/ui/carousel.tsx
  • kits/agentic/mockai/components/ui/chart.tsx
  • kits/agentic/mockai/components/ui/checkbox.tsx
  • kits/agentic/mockai/components/ui/collapsible.tsx
  • kits/agentic/mockai/components/ui/command.tsx
  • kits/agentic/mockai/components/ui/context-menu.tsx
  • kits/agentic/mockai/components/ui/dialog.tsx
  • kits/agentic/mockai/components/ui/drawer.tsx
  • kits/agentic/mockai/components/ui/dropdown-menu.tsx
  • kits/agentic/mockai/components/ui/empty.tsx
  • kits/agentic/mockai/components/ui/field.tsx
  • kits/agentic/mockai/components/ui/form.tsx
  • kits/agentic/mockai/components/ui/hover-card.tsx
  • kits/agentic/mockai/components/ui/input-group.tsx
  • kits/agentic/mockai/components/ui/input-otp.tsx
  • kits/agentic/mockai/components/ui/input.tsx
  • kits/agentic/mockai/components/ui/item.tsx
  • kits/agentic/mockai/components/ui/kbd.tsx
  • kits/agentic/mockai/components/ui/label.tsx
  • kits/agentic/mockai/components/ui/menubar.tsx
  • kits/agentic/mockai/components/ui/navigation-menu.tsx
  • kits/agentic/mockai/components/ui/pagination.tsx
  • kits/agentic/mockai/components/ui/popover.tsx
  • kits/agentic/mockai/components/ui/progress.tsx
  • kits/agentic/mockai/components/ui/radio-group.tsx
  • kits/agentic/mockai/components/ui/resizable.tsx
  • kits/agentic/mockai/components/ui/scroll-area.tsx
  • kits/agentic/mockai/components/ui/select.tsx
  • kits/agentic/mockai/components/ui/separator.tsx
  • kits/agentic/mockai/components/ui/sheet.tsx
  • kits/agentic/mockai/components/ui/sidebar.tsx
  • kits/agentic/mockai/components/ui/skeleton.tsx
  • kits/agentic/mockai/components/ui/slider.tsx
  • kits/agentic/mockai/components/ui/sonner.tsx
  • kits/agentic/mockai/components/ui/spinner.tsx
  • kits/agentic/mockai/components/ui/switch.tsx
  • kits/agentic/mockai/components/ui/table.tsx
  • kits/agentic/mockai/components/ui/tabs.tsx
  • kits/agentic/mockai/components/ui/textarea.tsx
  • kits/agentic/mockai/components/ui/toast.tsx
  • kits/agentic/mockai/components/ui/toaster.tsx
  • kits/agentic/mockai/components/ui/toggle-group.tsx
  • kits/agentic/mockai/components/ui/toggle.tsx
  • kits/agentic/mockai/components/ui/tooltip.tsx
  • kits/agentic/mockai/components/ui/use-mobile.tsx
  • kits/agentic/mockai/components/ui/use-toast.ts
  • kits/agentic/mockai/config.json
  • kits/agentic/mockai/flows/agentic-generate-content/README.md
  • kits/agentic/mockai/flows/agentic-generate-content/config.json
  • kits/agentic/mockai/flows/agentic-generate-content/inputs.json
  • kits/agentic/mockai/flows/agentic-generate-content/meta.json
  • kits/agentic/mockai/flows/feedback-flow/README.md
  • kits/agentic/mockai/flows/feedback-flow/config.json
  • kits/agentic/mockai/flows/feedback-flow/inputs.json
  • kits/agentic/mockai/flows/feedback-flow/meta.json
  • kits/agentic/mockai/flows/question-flow/README.md
  • kits/agentic/mockai/flows/question-flow/config.json
  • kits/agentic/mockai/flows/question-flow/inputs.json
  • kits/agentic/mockai/flows/question-flow/meta.json
  • kits/agentic/mockai/hooks/use-mobile.ts
  • kits/agentic/mockai/hooks/use-toast.ts
  • kits/agentic/mockai/lib/lamatic-client.ts
  • kits/agentic/mockai/lib/utils.ts
  • kits/agentic/mockai/next.config.mjs
  • kits/agentic/mockai/orchestrate.js
  • kits/agentic/mockai/package.json
  • kits/agentic/mockai/postcss.config.mjs
  • kits/agentic/mockai/styles/globals.css
  • kits/agentic/mockai/tsconfig.json

Comment thread kits/agentic/mockai/actions/orchestrate.ts Outdated
Comment thread kits/agentic/mockai/actions/orchestrate.ts
Comment thread kits/agentic/mockai/app/page.tsx
Comment thread kits/agentic/mockai/components/ui/AspectRatio.tsx
Comment thread kits/agentic/mockai/components/ui/collapsible.tsx Outdated
Comment thread kits/agentic/mockai/flows/feedback-flow/config.json
Comment thread kits/agentic/mockai/lib/lamatic-client.ts Outdated
Comment thread kits/agentic/mockai/package.json
Comment thread kits/agentic/mockai/README.md
Comment thread kits/agentic/mockai/README.md
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
kits/agentic/mockai/app/page.tsx (1)

374-375: Remove unused visualizerScale computation.

visualizerScale is computed on line 375 but never used in the rendered JSX. This appears to be leftover code from when the audio visualizer UI was present.

🧹 Proposed fix
     const isLastQuestion = currentQuestionIndex === questions.length - 1

     const displayAnswer = currentAnswer + (interimResult ? (currentAnswer.endsWith(" ") ? "" : " ") + interimResult : "")
-
-    // Calculate a dynamic scale for the visualizer pulse (1 to 1.5 roughly)
-    const visualizerScale = 1 + Math.min(volume / 50, 0.5)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8cddd5c6-76ee-4267-9402-e9c24968e6a0

📥 Commits

Reviewing files that changed from the base of the PR and between a92235c and 7aca27f.

📒 Files selected for processing (1)
  • kits/agentic/mockai/app/page.tsx

@amanintech
Copy link
Copy Markdown
Member

Agent @sreecharan1306 can you please resolve all the comments to go to next steps.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

PR Validation Results

New Contributions Detected

  • Kit: kits/agentic/mockai

Check Results

Check Status
No edits to existing projects ✅ Pass
Required root files present ✅ Pass
Flow folder structure valid ✅ Pass
No changes outside contribution dirs ✅ Pass

🎉 All checks passed! This contribution follows the AgentKit structure.

@sreecharan1306
Copy link
Copy Markdown
Author

@amanintech I have resolved all the comments successfully, please do check.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sreecharan1306
Copy link
Copy Markdown
Author

Hi @amanintech , I have resolved all the comments
Can you please look into them?
I am desperately waiting for the results

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Copy Markdown

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (4)
kits/agentic/mockai/components/ui/collapsible.tsx (1)

6-34: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Excellent field work on the API corrections, operative.

The Radix Collapsible primitives are now being accessed correctly—CollapsiblePrimitive.Trigger (line 16) and CollapsiblePrimitive.Content (line 27) match the official Radix export specifications. Your data-slot attributes provide clean styling hooks, and the wrapper pattern is consistent with your other UI components.

However, one item from the previous briefing remains unresolved: the filename collapsible.tsx must be renamed to Collapsible.tsx to comply with the PascalCase directive for React component files in the components/ directory. As per coding guidelines: "Use PascalCase for React component filenames in the components/ directory".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/collapsible.tsx` around lines 6 - 34,
Rename the file from collapsible.tsx to PascalCase Collapsible.tsx so it matches
the project's React component filename convention; ensure imported/exports of
the components Collapsible, CollapsibleTrigger, and CollapsibleContent remain
correct after the rename and update any imports elsewhere that reference the old
filename to import from "Collapsible" instead of "collapsible".
kits/agentic/mockai/actions/orchestrate.ts (1)

137-137: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the same log sanitization here.

The evaluation flow logs the full error object, which could expose candidate responses, feedback content, or API credentials. Use the same selective logging pattern: flow identifier, error classification, and message only.

🔒 Proposed sanitized logging
-    console.error("[v0] Evaluation error:", error)
+    console.error("[v0] Evaluation error:", {
+      flow: "feedback",
+      errorType: error instanceof z.ZodError ? "validation" : error instanceof Error ? "execution" : "unknown",
+      message: error instanceof Error ? error.message : "Unknown error"
+    })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/actions/orchestrate.ts` at line 137, Replace the raw
console.error("[v0] Evaluation error:", error) in
kits/agentic/mockai/actions/orchestrate.ts with a sanitized log that only
records the flow identifier, an error classification and the error message (not
the full error object or stack). For example, call the same logging pattern used
elsewhere (e.g., processLogger.error or a sanitizeError helper) to emit
something like "[v0] Evaluation error: <error.message>" or "[v0] Evaluation
error (EvaluationError): <message>" so candidate content, feedback, or secrets
are not logged.
kits/agentic/mockai/components/ui/tooltip.tsx (1)

8-23: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission-critical: the tooltip wrappers are wired to the wrong Radix primitives.

TooltipProvider renders TooltipPrimitive.Root on Line 12, so delayDuration from the sidebar-level provider never applies. Then Tooltip adds another provider boundary on Lines 19-21, which still defeats shared tooltip timing behavior across instances.

🛠️ Suggested fix
 function TooltipProvider({
   delayDuration = 0,
   ...props
 }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
-  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
+  return (
+    <TooltipPrimitive.Provider
+      data-slot="tooltip-provider"
+      delayDuration={delayDuration}
+      {...props}
+    />
+  )
 }
 
 function Tooltip({
   ...props
 }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
-  return (
-    <TooltipProvider>
-      <TooltipPrimitive.Root data-slot="tooltip" {...props} />
-    </TooltipProvider>
-  )
+  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
 }
Radix UI Tooltip docs: what is the difference between Tooltip.Provider and Tooltip.Root, and which component applies delayDuration/skipDelayDuration to descendant tooltips?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/tooltip.tsx` around lines 8 - 23,
TooltipProvider is rendering TooltipPrimitive.Root instead of
TooltipPrimitive.Provider so the delayDuration prop never applies, and Tooltip
incorrectly wraps each instance in a provider; change TooltipProvider to return
<TooltipPrimitive.Provider delayDuration={delayDuration} {...props}/> (ensure it
forwards children) and change Tooltip to render only <TooltipPrimitive.Root
data-slot="tooltip" {...props} /> (remove the extra TooltipProvider wrapper) so
shared timing from TooltipPrimitive.Provider (delayDuration/skipDelayDuration)
applies to descendant tooltips; update function signatures for TooltipProvider
and Tooltip to accept and forward children accordingly.
kits/agentic/mockai/components/ui/sidebar.tsx (1)

76-95: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission update: the controlled setOpen path still resolves from a stale closure.

Line 84 writes the cookie from the captured open value before the parent state has advanced. Two synchronous setOpen((prev) => !prev) calls can still compute the same next value and persist the wrong cookie state.

🛠️ Suggested fix
   const setOpen = React.useCallback(
     (value: boolean | ((value: boolean) => boolean)) => {
-      const valueSetter = typeof value === 'function' ? value : () => value
-
       if (setOpenProp) {
-        setOpenProp(valueSetter)
-
-        // This sets the cookie to keep the sidebar state.
-        document.cookie = `${SIDEBAR_COOKIE_NAME}=${valueSetter(open)}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
+        setOpenProp(value)
       } else {
-        _setOpen((prev) => {
-          const openState = valueSetter(prev)
-
-          // This sets the cookie to keep the sidebar state.
-          document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
-          return openState
-        })
+        _setOpen(value)
       }
     },
-    [setOpenProp, open],
+    [setOpenProp],
   )
+
+  React.useEffect(() => {
+    document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
+  }, [open])
React docs: when forwarding a SetStateAction<boolean> to a parent setter, should side effects like cookie writes run from the stale closure, or after the state value has actually changed?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/sidebar.tsx` around lines 76 - 95, The
controlled path writes the cookie using the captured open value (stale closure);
change the setOpen logic so when setOpenProp exists you always call setOpenProp
with an updater function that computes next from prev and writes the cookie
there (e.g. setOpenProp((prev) => { const next = typeof value === 'function' ?
value(prev) : value; document.cookie = `${SIDEBAR_COOKIE_NAME}=${next}; path=/;
max-age=${SIDEBAR_COOKIE_MAX_AGE}`; return next; })), and remove the cookie
write that uses the outer open variable; keep the uncontrolled _setOpen branch
as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@kits/agentic/mockai/actions/orchestrate.ts`:
- Line 58: The current console.error call in orchestrate.ts logs the full error
object (console.error("[v0] Generation error:", error)) which may contain
sensitive data; replace it with a sanitized server-side log that prints only the
sanitized error message, the flow name/ID, and the error type/name. Use the same
sanitizer function you call when sending errors to the client (e.g.,
sanitizeErrorMessage or the existing sanitization utility used elsewhere in this
file) to produce the message, then log exactly: flow identifier, error.name (or
error.type), and the sanitized message instead of the full error object.

In `@kits/agentic/mockai/components/ui/form.tsx`:
- Line 1: Rename the component file from form.tsx to Form.tsx to follow
PascalCase conventions, then update every import/export that references "form"
to use "Form" (e.g., import/require and any barrel/index exports) so module
resolution still works; ensure default or named export inside the file still
matches the component name if present and run a quick build or typecheck to
catch any remaining case-sensitive import issues.

In `@kits/agentic/mockai/components/ui/sidebar.tsx`:
- Around line 1-743: The file is named with a lowercase module name; rename the
module file from "sidebar.tsx" to "Sidebar.tsx" to follow PascalCase convention
for components; ensure the file still exports the same named exports (Sidebar,
SidebarProvider, useSidebar, SidebarTrigger, SidebarRail, etc.), update all
import sites across the codebase to reference the new filename (e.g., import {
Sidebar } from '.../components/Sidebar') and run a quick search/replace for any
relative import paths or tests referencing "sidebar.tsx" to avoid broken
imports.
- Around line 196-217: The mobile branch's SheetContent hardcodes its className
and ignores the incoming className prop, breaking caller styling; update the
isMobile branch to merge the passed className (from props) with the existing
mobile classes before passing to SheetContent (e.g., compute a combined class
string or use a utility like clsx) and ensure you still spread remaining props
and keep style, side, data attributes, and children (refer to SheetContent,
isMobile, openMobile, setOpenMobile, SIDEBAR_WIDTH_MOBILE, side, and
props.className).
- Around line 626-629: The current React.useMemo that computes width using
Math.random() (the width constant in this component) causes server/client
hydration mismatches; replace the deterministic-in-render approach by moving
randomness to a browser-only effect or computing a deterministic value from
stable input. Concretely: remove Math.random() from the React.useMemo that
defines width and either (a) initialize width via useState and set a random
value inside useEffect (so it only runs on the client after mount) or (b) derive
width deterministically from a stable prop/id (e.g., hash an id/index) instead
of Math.random(); update any references to the width constant and keep the
existing React.useMemo only for pure deterministic calculations.

In `@kits/agentic/mockai/components/ui/tooltip.tsx`:
- Around line 1-55: The file is named using lowercase but must follow the repo’s
PascalCase convention; rename the module file to Tooltip.tsx and ensure the
exported components (Tooltip, TooltipProvider, TooltipTrigger, TooltipContent)
remain unchanged; update any imports across the codebase that reference the old
lowercase filename to use Tooltip (or adjust barrel exports) and perform the git
mv to preserve history so builds and type imports continue to resolve correctly.

In `@kits/agentic/mockai/PR_DESCRIPTION.md`:
- Around line 1-10: The PR_DESCRIPTION.md headings violate markdown conventions:
change the first line to a level-1 heading (replace "## Title: feat(mockai):
..." with "# feat(mockai): transform generic app into Mockai Interview prep
platform") and ensure there is a blank line before and after each heading (for
"📖 Description" and "🧪 How to Test") so the renderer parses them correctly;
keep the existing heading text but adjust them to use consistent heading levels
(e.g., "## 📖 Description" and "## 🧪 How to Test" or lower as appropriate) and
add a blank line between paragraphs and each heading to meet field manual
standards.

---

Duplicate comments:
In `@kits/agentic/mockai/actions/orchestrate.ts`:
- Line 137: Replace the raw console.error("[v0] Evaluation error:", error) in
kits/agentic/mockai/actions/orchestrate.ts with a sanitized log that only
records the flow identifier, an error classification and the error message (not
the full error object or stack). For example, call the same logging pattern used
elsewhere (e.g., processLogger.error or a sanitizeError helper) to emit
something like "[v0] Evaluation error: <error.message>" or "[v0] Evaluation
error (EvaluationError): <message>" so candidate content, feedback, or secrets
are not logged.

In `@kits/agentic/mockai/components/ui/collapsible.tsx`:
- Around line 6-34: Rename the file from collapsible.tsx to PascalCase
Collapsible.tsx so it matches the project's React component filename convention;
ensure imported/exports of the components Collapsible, CollapsibleTrigger, and
CollapsibleContent remain correct after the rename and update any imports
elsewhere that reference the old filename to import from "Collapsible" instead
of "collapsible".

In `@kits/agentic/mockai/components/ui/sidebar.tsx`:
- Around line 76-95: The controlled path writes the cookie using the captured
open value (stale closure); change the setOpen logic so when setOpenProp exists
you always call setOpenProp with an updater function that computes next from
prev and writes the cookie there (e.g. setOpenProp((prev) => { const next =
typeof value === 'function' ? value(prev) : value; document.cookie =
`${SIDEBAR_COOKIE_NAME}=${next}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
return next; })), and remove the cookie write that uses the outer open variable;
keep the uncontrolled _setOpen branch as-is.

In `@kits/agentic/mockai/components/ui/tooltip.tsx`:
- Around line 8-23: TooltipProvider is rendering TooltipPrimitive.Root instead
of TooltipPrimitive.Provider so the delayDuration prop never applies, and
Tooltip incorrectly wraps each instance in a provider; change TooltipProvider to
return <TooltipPrimitive.Provider delayDuration={delayDuration} {...props}/>
(ensure it forwards children) and change Tooltip to render only
<TooltipPrimitive.Root data-slot="tooltip" {...props} /> (remove the extra
TooltipProvider wrapper) so shared timing from TooltipPrimitive.Provider
(delayDuration/skipDelayDuration) applies to descendant tooltips; update
function signatures for TooltipProvider and Tooltip to accept and forward
children accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 579bb55a-56ff-4e9b-b0da-48931fb62eba

📥 Commits

Reviewing files that changed from the base of the PR and between 7aca27f and 4ef625e.

📒 Files selected for processing (10)
  • kits/agentic/mockai/.gitignore
  • kits/agentic/mockai/PR_DESCRIPTION.md
  • kits/agentic/mockai/actions/orchestrate.ts
  • kits/agentic/mockai/components/ui/AspectRatio.tsx
  • kits/agentic/mockai/components/ui/collapsible.tsx
  • kits/agentic/mockai/components/ui/form.tsx
  • kits/agentic/mockai/components/ui/sidebar.tsx
  • kits/agentic/mockai/components/ui/tooltip.tsx
  • kits/agentic/mockai/config.json
  • kits/agentic/mockai/lib/lamatic-client.ts

questions,
}
} catch (error) {
console.error("[v0] Generation error:", error)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sanitize error logs to prevent intelligence leakage.

This error log writes the full error object to the server, which may contain sensitive interview content (job descriptions, candidate answers, API tokens in stack traces, or full Lamatic payloads). You're already sanitizing the error message before sending it to the client—apply the same discipline to server-side logging. Log only the sanitized message, flow name, and error type.

🔒 Proposed sanitized logging
-    console.error("[v0] Generation error:", error)
+    console.error("[v0] Generation error:", {
+      flow: "question",
+      errorType: error instanceof z.ZodError ? "validation" : error instanceof Error ? "execution" : "unknown",
+      message: error instanceof Error ? error.message : "Unknown error"
+    })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.error("[v0] Generation error:", error)
console.error("[v0] Generation error:", {
flow: "question",
errorType: error instanceof z.ZodError ? "validation" : error instanceof Error ? "execution" : "unknown",
message: error instanceof Error ? error.message : "Unknown error"
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/actions/orchestrate.ts` at line 58, The current
console.error call in orchestrate.ts logs the full error object
(console.error("[v0] Generation error:", error)) which may contain sensitive
data; replace it with a sanitized server-side log that prints only the sanitized
error message, the flow name/ID, and the error type/name. Use the same sanitizer
function you call when sending errors to the client (e.g., sanitizeErrorMessage
or the existing sanitization utility used elsewhere in this file) to produce the
message, then log exactly: flow identifier, error.name (or error.type), and the
sanitized message instead of the full error object.

@@ -0,0 +1,167 @@
'use client'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Intel alert: Filename doesn't match operational guidelines.

Agent, your component file is currently designated as form.tsx, but the mission protocols require PascalCase for all React component files in the components/ directory. Rename this asset to Form.tsx to maintain consistency with project standards and complete the mission parameters.

As per coding guidelines: "Use PascalCase for React component filenames in the components/ directory"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/form.tsx` at line 1, Rename the component
file from form.tsx to Form.tsx to follow PascalCase conventions, then update
every import/export that references "form" to use "Form" (e.g., import/require
and any barrel/index exports) so module resolution still works; ensure default
or named export inside the file still matches the component name if present and
run a quick build or typecheck to catch any remaining case-sensitive import
issues.

Comment on lines +1 to +743
'use client'

import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, VariantProps } from 'class-variance-authority'
import { PanelLeftIcon } from 'lucide-react'

import { useIsMobile } from '@/hooks/use-mobile'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator'
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet'
import { Skeleton } from '@/components/ui/skeleton'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'

const SIDEBAR_COOKIE_NAME = 'sidebar_state'
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
const SIDEBAR_WIDTH = '16rem'
const SIDEBAR_WIDTH_MOBILE = '18rem'
const SIDEBAR_WIDTH_ICON = '3rem'
const SIDEBAR_KEYBOARD_SHORTCUT = 'b'

type SidebarContextProps = {
state: 'expanded' | 'collapsed'
open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
openMobile: boolean
setOpenMobile: React.Dispatch<React.SetStateAction<boolean>>
isMobile: boolean
toggleSidebar: () => void
}

const SidebarContext = React.createContext<SidebarContextProps | null>(null)

function useSidebar() {
const context = React.useContext(SidebarContext)
if (!context) {
throw new Error('useSidebar must be used within a SidebarProvider.')
}

return context
}

function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
style,
children,
...props
}: React.ComponentProps<'div'> & {
defaultOpen?: boolean
open?: boolean
onOpenChange?: React.Dispatch<React.SetStateAction<boolean>>
}) {
const isMobile = useIsMobile()
const [openMobile, setOpenMobile] = React.useState(false)

// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen)
const open = openProp ?? _open
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const valueSetter = typeof value === 'function' ? value : () => value

if (setOpenProp) {
setOpenProp(valueSetter)

// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${valueSetter(open)}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
} else {
_setOpen((prev) => {
const openState = valueSetter(prev)

// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
return openState
})
}
},
[setOpenProp, open],
)

// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
}, [isMobile, setOpen, setOpenMobile])

// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const target = event.target as HTMLElement | null
const isEditable =
!!target?.closest('input, textarea, select, [contenteditable="true"]') ||
target?.isContentEditable

if (isEditable) return;
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault()
toggleSidebar()
}
}

window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [toggleSidebar])

// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? 'expanded' : 'collapsed'

const contextValue = React.useMemo<SidebarContextProps>(
() => ({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
)

return (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>
<div
data-slot="sidebar-wrapper"
style={
{
'--sidebar-width': SIDEBAR_WIDTH,
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
...style,
} as React.CSSProperties
}
className={cn(
'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full',
className,
)}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
)
}

function Sidebar({
side = 'left',
variant = 'sidebar',
collapsible = 'offcanvas',
className,
children,
...props
}: React.ComponentProps<'div'> & {
side?: 'left' | 'right'
variant?: 'sidebar' | 'floating' | 'inset'
collapsible?: 'offcanvas' | 'icon' | 'none'
}) {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar()

if (collapsible === 'none') {
return (
<div
data-slot="sidebar"
className={cn(
'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col',
className,
)}
{...props}
>
{children}
</div>
)
}

if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
data-sidebar="sidebar"
data-slot="sidebar"
data-mobile="true"
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
style={
{
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
} as React.CSSProperties
}
side={side}
>
<SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<div className="flex h-full w-full flex-col">{children}</div>
</SheetContent>
</Sheet>
)
}

return (
<div
className="group peer text-sidebar-foreground hidden md:block"
data-state={state}
data-collapsible={state === 'collapsed' ? collapsible : ''}
data-variant={variant}
data-side={side}
data-slot="sidebar"
>
{/* This is what handles the sidebar gap on desktop */}
<div
data-slot="sidebar-gap"
className={cn(
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
'group-data-[collapsible=offcanvas]:w-0',
'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset'
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',
)}
/>
<div
data-slot="sidebar-container"
className={cn(
'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',
side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
// Adjust the padding for floating and inset variants.
variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
className,
)}
{...props}
>
<div
data-sidebar="sidebar"
data-slot="sidebar-inner"
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
>
{children}
</div>
</div>
</div>
)
}

function SidebarTrigger({
className,
onClick,
...props
}: React.ComponentProps<typeof Button>) {
const { toggleSidebar } = useSidebar()

return (
<Button
data-sidebar="trigger"
data-slot="sidebar-trigger"
variant="ghost"
size="icon"
className={cn('size-7', className)}
onClick={(event) => {
onClick?.(event)
toggleSidebar()
}}
{...props}
>
<PanelLeftIcon />
<span className="sr-only">Toggle Sidebar</span>
</Button>
)
}

function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
const { toggleSidebar } = useSidebar()

return (
<button
type="button"
data-sidebar="rail"
data-slot="sidebar-rail"
aria-label="Toggle Sidebar"
tabIndex={-1}
onClick={toggleSidebar}
title="Toggle Sidebar"
className={cn(
'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex',
'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
className,
)}
{...props}
/>
)
}

function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
return (
<main
data-slot="sidebar-inset"
className={cn(
'bg-background relative flex w-full flex-1 flex-col',
'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',
className,
)}
{...props}
/>
)
}

function SidebarInput({
className,
...props
}: React.ComponentProps<typeof Input>) {
return (
<Input
data-slot="sidebar-input"
data-sidebar="input"
className={cn('bg-background h-8 w-full shadow-none', className)}
{...props}
/>
)
}

function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-header"
data-sidebar="header"
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
)
}

function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-footer"
data-sidebar="footer"
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
)
}

function SidebarSeparator({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="sidebar-separator"
data-sidebar="separator"
className={cn('bg-sidebar-border mx-2 w-auto', className)}
{...props}
/>
)
}

function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-content"
data-sidebar="content"
className={cn(
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
className,
)}
{...props}
/>
)
}

function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-group"
data-sidebar="group"
className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
{...props}
/>
)
}

function SidebarGroupLabel({
className,
asChild = false,
...props
}: React.ComponentProps<'div'> & { asChild?: boolean }) {
const Comp = asChild ? Slot : 'div'

return (
<Comp
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
className,
)}
{...props}
/>
)
}

function SidebarGroupAction({
className,
asChild = false,
...props
}: React.ComponentProps<'button'> & { asChild?: boolean }) {
const Comp = asChild ? Slot : 'button'

return (
<Comp
{...(!asChild ? { type: 'button' as const } : {})}
data-slot="sidebar-group-action"
data-sidebar="group-action"
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.
'after:absolute after:-inset-2 md:after:hidden',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
)
}

function SidebarGroupContent({
className,
...props
}: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-group-content"
data-sidebar="group-content"
className={cn('w-full text-sm', className)}
{...props}
/>
)
}

function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
return (
<ul
data-slot="sidebar-menu"
data-sidebar="menu"
className={cn('flex w-full min-w-0 flex-col gap-1', className)}
{...props}
/>
)
}

function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
return (
<li
data-slot="sidebar-menu-item"
data-sidebar="menu-item"
className={cn('group/menu-item relative', className)}
{...props}
/>
)
}

const sidebarMenuButtonVariants = cva(
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
{
variants: {
variant: {
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
outline:
'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
},
size: {
default: 'h-8 text-sm',
sm: 'h-7 text-xs',
lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)

function SidebarMenuButton({
asChild = false,
isActive = false,
variant = 'default',
size = 'default',
tooltip,
className,
...props
}: React.ComponentProps<'button'> & {
asChild?: boolean
isActive?: boolean
tooltip?: string | React.ComponentProps<typeof TooltipContent>
} & VariantProps<typeof sidebarMenuButtonVariants>) {
const Comp = asChild ? Slot : 'button'
const { isMobile, state } = useSidebar()

const button = (
<Comp
{...(!asChild ? { type: 'button' as const } : {})}
data-slot="sidebar-menu-button"
data-sidebar="menu-button"
data-size={size}
data-active={isActive}
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
{...props}
/>
)

if (!tooltip) {
return button
}

if (typeof tooltip === 'string') {
tooltip = {
children: tooltip,
}
}

return (
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent
side="right"
align="center"
hidden={state !== 'collapsed' || isMobile}
{...tooltip}
/>
</Tooltip>
)
}

function SidebarMenuAction({
className,
asChild = false,
showOnHover = false,
...props
}: React.ComponentProps<'button'> & {
asChild?: boolean
showOnHover?: boolean
}) {
const Comp = asChild ? Slot : 'button'

return (
<Comp
{...(!asChild ? { type: 'button' as const } : {})}
data-slot="sidebar-menu-action"
data-sidebar="menu-action"
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.
'after:absolute after:-inset-2 md:after:hidden',
'peer-data-[size=sm]/menu-button:top-1',
'peer-data-[size=default]/menu-button:top-1.5',
'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden',
showOnHover &&
'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',
className,
)}
{...props}
/>
)
}

function SidebarMenuBadge({
className,
...props
}: React.ComponentProps<'div'>) {
return (
<div
data-slot="sidebar-menu-badge"
data-sidebar="menu-badge"
className={cn(
'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',
'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
'peer-data-[size=sm]/menu-button:top-1',
'peer-data-[size=default]/menu-button:top-1.5',
'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
)
}

function SidebarMenuSkeleton({
className,
showIcon = false,
...props
}: React.ComponentProps<'div'> & {
showIcon?: boolean
}) {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])

return (
<div
data-slot="sidebar-menu-skeleton"
data-sidebar="menu-skeleton"
className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
{...props}
>
{showIcon && (
<Skeleton
className="size-4 rounded-md"
data-sidebar="menu-skeleton-icon"
/>
)}
<Skeleton
className="h-4 max-w-(--skeleton-width) flex-1"
data-sidebar="menu-skeleton-text"
style={
{
'--skeleton-width': width,
} as React.CSSProperties
}
/>
</div>
)
}

function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
return (
<ul
data-slot="sidebar-menu-sub"
data-sidebar="menu-sub"
className={cn(
'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
)
}

function SidebarMenuSubItem({
className,
...props
}: React.ComponentProps<'li'>) {
return (
<li
data-slot="sidebar-menu-sub-item"
data-sidebar="menu-sub-item"
className={cn('group/menu-sub-item relative', className)}
{...props}
/>
)
}

function SidebarMenuSubButton({
asChild = false,
size = 'md',
isActive = false,
className,
...props
}: React.ComponentProps<'a'> & {
asChild?: boolean
size?: 'sm' | 'md'
isActive?: boolean
}) {
const Comp = asChild ? Slot : 'a'

return (
<Comp
data-slot="sidebar-menu-sub-button"
data-sidebar="menu-sub-button"
data-size={size}
data-active={isActive}
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
size === 'sm' && 'text-xs',
size === 'md' && 'text-sm',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
)
}

export {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Mission note: rename this module to Sidebar.tsx.

This new component file is also under components/, so it should follow the same PascalCase filename convention.

As per coding guidelines, kits/**/components/**/*.{tsx,ts}: Use PascalCase for React component filenames in the components/ directory.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/sidebar.tsx` around lines 1 - 743, The file
is named with a lowercase module name; rename the module file from "sidebar.tsx"
to "Sidebar.tsx" to follow PascalCase convention for components; ensure the file
still exports the same named exports (Sidebar, SidebarProvider, useSidebar,
SidebarTrigger, SidebarRail, etc.), update all import sites across the codebase
to reference the new filename (e.g., import { Sidebar } from
'.../components/Sidebar') and run a quick search/replace for any relative import
paths or tests referencing "sidebar.tsx" to avoid broken imports.

Comment on lines +196 to +217
if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
data-sidebar="sidebar"
data-slot="sidebar"
data-mobile="true"
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
style={
{
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
} as React.CSSProperties
}
side={side}
>
<SheetHeader className="sr-only">
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<div className="flex h-full w-full flex-col">{children}</div>
</SheetContent>
</Sheet>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mission detail: className disappears in the mobile branch.

Caller styles are applied in the desktop paths, but the mobile SheetContent hardcodes its classes and never merges className, so responsive overrides silently stop working on mobile.

🛠️ Suggested fix
         <SheetContent
           data-sidebar="sidebar"
           data-slot="sidebar"
           data-mobile="true"
-          className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
+          className={cn(
+            'bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden',
+            className,
+          )}
           style={
             {
               '--sidebar-width': SIDEBAR_WIDTH_MOBILE,
             } as React.CSSProperties
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/sidebar.tsx` around lines 196 - 217, The
mobile branch's SheetContent hardcodes its className and ignores the incoming
className prop, breaking caller styling; update the isMobile branch to merge the
passed className (from props) with the existing mobile classes before passing to
SheetContent (e.g., compute a combined class string or use a utility like clsx)
and ensure you still spread remaining props and keep style, side, data
attributes, and children (refer to SheetContent, isMobile, openMobile,
setOpenMobile, SIDEBAR_WIDTH_MOBILE, side, and props.className).

Comment on lines +626 to +629
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Next.js App Router hydration: can Math.random() inside a 'use client' component render path still cause a server/client markup mismatch?

💡 Result:

Yes—Math.random inside a 'use client' component can still lead to a server/client markup mismatch (hydration error) in Next.js App Router, because Next.js may prerender the Client Component on the server (by simulating the client environment). If Math.random is called during that prerender, the random value will differ from what the browser computes during hydration, so the initial HTML doesn’t match the hydrated React tree. [1] Next.js documents this behavior specifically for Math.random: on an initial page visit, Next.js SSR/prerenders HTML even for Client Components, but if a Client Component accesses a random source during prerender, Next.js can’t include it in the prerendered HTML and therefore may require a Suspense boundary or other handling. [1] How to avoid the mismatch: 1) Only access Math.random in the browser (e.g., inside useEffect or an event handler), since React does not run effects during server rendering and there are no events during server rendering. [1] 2) Or, if you truly must compute synchronously during render, wrap the component that calls Math.random in a Suspense boundary so Next.js can prerender a fallback and then render the random value on the client. [1] So the presence of 'use client' alone does not guarantee it runs only on the client for the initial render in App Router; it still participates in SSR/prerendering unless you defer the random call appropriately. [1][2]

Citations:


🏁 Script executed:

# Locate and examine the sidebar.tsx file
fd -t f "sidebar.tsx" kits/agentic/mockai/components/ui/

# Get context around lines 626-629
cat -n kits/agentic/mockai/components/ui/sidebar.tsx | sed -n '600,650p'

# Check if the file has 'use client' directive
head -20 kits/agentic/mockai/components/ui/sidebar.tsx

Repository: Lamatic/AgentKit

Length of output: 2602


🏁 Script executed:

# Search for uses of SidebarMenuSkeleton
rg "SidebarMenuSkeleton" kits/agentic/mockai/components/ui/ -A 2 -B 2

# Check if sidebar is used in server/client boundary context
fd -t f "sidebar.tsx" kits/ | head -10
rg "from.*sidebar" kits/agentic/mockai/ -t tsx -t ts | head -20

Repository: Lamatic/AgentKit

Length of output: 1240


🏁 Script executed:

# Search for actual imports and usages of SidebarMenuSkeleton across all kits
rg "SidebarMenuSkeleton" kits/ --type-list | head -5
rg "import.*SidebarMenuSkeleton\|from.*sidebar" kits/ -l 2>/dev/null | head -10

# Check if the sidebar is typically used in layouts (which are server-rendered in App Router by default)
fd -t f "layout.tsx" kits/agentic/mockai/ | head -5
cat kits/agentic/mockai/app/layout.tsx 2>/dev/null | head -50

Repository: Lamatic/AgentKit

Length of output: 1198


🏁 Script executed:

# Search for actual imports of SidebarMenuSkeleton
rg "import.*SidebarMenuSkeleton" kits/ -A 1

# Search for usages of the component
rg "<SidebarMenuSkeleton" kits/ -B 2 -A 2

Repository: Lamatic/AgentKit

Length of output: 42


🏁 Script executed:

# Check the export statement in sidebar.tsx to confirm SidebarMenuSkeleton is exported
rg "export.*SidebarMenuSkeleton" kits/agentic/mockai/components/ui/sidebar.tsx -B 2 -A 2

# Also check the entire exports from this file
rg "^export" kits/agentic/mockai/components/ui/sidebar.tsx

Repository: Lamatic/AgentKit

Length of output: 69


🏁 Script executed:

# Get the full export section from sidebar.tsx
tail -100 kits/agentic/mockai/components/ui/sidebar.tsx | head -80

Repository: Lamatic/AgentKit

Length of output: 2281


🏁 Script executed:

# Get the complete export list
tail -50 kits/agentic/mockai/components/ui/sidebar.tsx

Repository: Lamatic/AgentKit

Length of output: 1556


This Math.random() call will trigger hydration mismatches in Next.js App Router.

Math.random() in the render path creates different markup between server prerender and client hydration—Next.js prerender simulates the client environment for 'use client' components, so the random value differs. The synchronous usage in the style prop compounds the risk. Replace with a deterministic value:

🛠️ Suggested fix
-  const width = React.useMemo(() => {
-    return `${Math.floor(Math.random() * 40) + 50}%`
-  }, [])
+  const width = '70%'

Alternatively, defer randomness to useEffect (browser-only) or wrap in a Suspense boundary if variation is required.

📝 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
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])
// Random width between 50 to 90%.
const width = '70%'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/sidebar.tsx` around lines 626 - 629, The
current React.useMemo that computes width using Math.random() (the width
constant in this component) causes server/client hydration mismatches; replace
the deterministic-in-render approach by moving randomness to a browser-only
effect or computing a deterministic value from stable input. Concretely: remove
Math.random() from the React.useMemo that defines width and either (a)
initialize width via useState and set a random value inside useEffect (so it
only runs on the client after mount) or (b) derive width deterministically from
a stable prop/id (e.g., hash an id/index) instead of Math.random(); update any
references to the width constant and keep the existing React.useMemo only for
pure deterministic calculations.

Comment on lines +1 to +55
'use client'

import * as React from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'

import { cn } from '@/lib/utils'

function TooltipProvider({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
}

function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return (
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
)
}

function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}

function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
'bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
className,
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
}

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Mission note: rename this module to Tooltip.tsx.

This new component file sits under components/, so the lowercase filename misses the repo’s PascalCase convention.

As per coding guidelines, kits/**/components/**/*.{tsx,ts}: Use PascalCase for React component filenames in the components/ directory.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/components/ui/tooltip.tsx` around lines 1 - 55, The file
is named using lowercase but must follow the repo’s PascalCase convention;
rename the module file to Tooltip.tsx and ensure the exported components
(Tooltip, TooltipProvider, TooltipTrigger, TooltipContent) remain unchanged;
update any imports across the codebase that reference the old lowercase filename
to use Tooltip (or adjust barrel exports) and perform the git mv to preserve
history so builds and type imports continue to resolve correctly.

Comment on lines +1 to +10
## Title: feat(mockai): transform generic app into Mockai Interview prep platform

### 📖 Description
This PR significantly transforms the initial `agentic/mockai` app into **Mockai**, a powerful 3-step AI mock interview application tailored for an engaging user assessment journey. It introduces a modern, dynamic UI, reliable real-time microphone dictation, intelligent audio visualizations, and robust Vercel deployment configurations.

### 🧪 How to Test
1. Pull the branch and run `npm install`.
2. Ensure your `.env` contains the keys `AGENTIC_QUESTION_FLOW_ID`, `AGENTIC_FEEDBACK_FLOW_ID` mapping to your Lamatic workflows.
3. Start the server with `npm run dev` and navigate to `http://localhost:3200` (ensure you use `localhost` instead of a network IP, otherwise browsers block mic permissions).
4. Run through the interview, utilizing the real-time "Record Voice" functionality, and verify the feedback metrics at the end.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Documentation formatting requires adjustment for field manual standards.

Your markdown structure violates standard heading hierarchy and spacing conventions. The first line should be a level-1 heading (single #), and all headings need blank lines above and below for proper document parsing and readability in mission briefs.

📝 Proposed formatting corrections
-## Title: feat(mockai): transform generic app into Mockai Interview prep platform
+# feat(mockai): transform generic app into Mockai Interview prep platform

-### 📖 Description
+
+## 📖 Description
+
 This PR significantly transforms the initial `agentic/mockai` app into **Mockai**, a powerful 3-step AI mock interview application tailored for an engaging user assessment journey. It introduces a modern, dynamic UI, reliable real-time microphone dictation, intelligent audio visualizations, and robust Vercel deployment configurations.

-### 🧪 How to Test
+
+## 🧪 How to Test
+
 1. Pull the branch and run `npm install`.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


[warning] 3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 6-6: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/agentic/mockai/PR_DESCRIPTION.md` around lines 1 - 10, The
PR_DESCRIPTION.md headings violate markdown conventions: change the first line
to a level-1 heading (replace "## Title: feat(mockai): ..." with "#
feat(mockai): transform generic app into Mockai Interview prep platform") and
ensure there is a blank line before and after each heading (for "📖 Description"
and "🧪 How to Test") so the renderer parses them correctly; keep the existing
heading text but adjust them to use consistent heading levels (e.g., "## 📖
Description" and "## 🧪 How to Test" or lower as appropriate) and add a blank
line between paragraphs and each heading to meet field manual standards.

@github-actions
Copy link
Copy Markdown

Hi @sreecharan1306! 👋

Before this PR can be reviewed by maintainers, please resolve all comments and requested changes from the CodeRabbit automated review.

Steps to follow:

  1. Read through all CodeRabbit comments carefully
  2. Address each issue raised (or reply explaining why you disagree)
  3. Push your fixes as new commits
  4. Once all issues are resolved, comment here so we can re-review

This helps keep the review process efficient for everyone. Thank you! 🙏

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants