Skip to content

Add Lamatic agentic travel planner kit#49

Closed
jeloskaisabel wants to merge 7 commits intoLamatic:mainfrom
jeloskaisabel:main
Closed

Add Lamatic agentic travel planner kit#49
jeloskaisabel wants to merge 7 commits intoLamatic:mainfrom
jeloskaisabel:main

Conversation

@jeloskaisabel
Copy link
Copy Markdown

@jeloskaisabel jeloskaisabel commented Oct 28, 2025

What: Introduces a multi-agent travel researcher and booking assistant that coordinates Lamatic flight, hotel, and activity agents to deliver a shareable itinerary, budget, and booking checklist for any destination.
Prerequisites: Lamatic account with a valid LAMATIC_API_KEY.
Steps to run locally:

cd templates/agentic/travel-planner
npm install
Create .env.local with LAMATIC_API_KEY=your_key
npm run dev and open http://localhost:3000/

Live preview: https://agent-kit-vert.vercel.app/
Lamatic flow ID: 3cc791a2-ca33-4e27-8791-ff386cef14b2

Summary by CodeRabbit

  • New Features

    • Added a complete travel planner application template with multi-agent orchestration for generating personalized travel itineraries.
    • Integrated comprehensive travel planning UI with real-time status updates, flight/accommodation options, activity recommendations, and budget breakdowns.
    • Implemented a full suite of reusable UI components built on Radix UI for consistent design patterns.
  • Chores

    • Configured project dependencies, build tools, and development environment setup.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

Adds a complete multi-agent travel planner template to templates/agentic/travel-planner/ featuring a Next.js application with Lamatic workflow orchestration, comprehensive Radix UI-based component library, server-side action handlers for travel planning, and full TypeScript configuration with build tooling integration.

Changes

Cohort / File(s) Summary
Configuration & Metadata
templates/agentic/travel-planner/{.env.example, .eslintrc.json, .gitignore, .npmrc, LICENSE, README.md}
Environment variable template, ESLint configuration extending Next.js presets, gitignore patterns, npm force-install setting, MIT license, and comprehensive documentation of features and setup.
Application Structure & Styling
templates/agentic/travel-planner/{app/layout.tsx, app/page.tsx, app/globals.css, styles/globals.css}
Root layout with metadata and Geist fonts, full-featured travel planner page component with form state and multi-section result rendering, and global Tailwind-based styling with dark/light theme CSS variables.
UI Component Library
templates/agentic/travel-planner/components/ui/{accordion, alert, alert-dialog, aspect-ratio, avatar, badge, breadcrumb, button, calendar, card, carousel, chart, checkbox, collapsible, command, context-menu, dialog, drawer, dropdown-menu, form, hover-card, input, input-otp, label, menubar, navigation-menu, pagination, popover, progress, radio-group, resizable, scroll-area, select, separator, sheet, sidebar, skeleton, slider, sonner, switch, table, tabs, textarea, toast, toaster, toggle, toggle-group, tooltip, use-mobile}.tsx
50+ Radix UI-based wrapper components with consistent styling, data-slot attributes, and theming support; includes complex stateful components (sidebar, carousel, chart) and utility hooks.
Core Components
templates/agentic/travel-planner/components/theme-provider.tsx
Client-side theme provider wrapper around next-themes.
Orchestration & Lamatic Integration
templates/agentic/travel-planner/actions/orchestrate.ts
Server action that defines travel planning data structures, dispatches Lamatic flow, normalizes responses into structured travel plans with extensive parsing utilities.
Lamatic Client & Config
templates/agentic/travel-planner/{lib/lamatic-client.ts, lamatic-config.json}
Lamatic client initialization with API key validation and configuration schema for multi-agent travel planner workflow.
Utilities & Hooks
templates/agentic/travel-planner/{lib/utils.ts, hooks/{use-mobile.ts, use-toast.ts}}
Tailwind class merging utility, mobile breakpoint detection hook, and toast notification state management.
Build Configuration & Package
templates/agentic/travel-planner/{package.json, tsconfig.json, next.config.mjs, postcss.config.mjs, components.json}
Project manifest with UI/frontend dependencies, TypeScript strict mode configuration, Next.js build optimizations, PostCSS Tailwind setup, and shadcn/ui component schema.
Deployment
templates/agentic/travel-planner/scripts/deploy.sh
Simple deployment script that installs dependencies and starts development server.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Page as TravelPlannerPage
    participant Form as Form Handler
    participant Action as planTrip Action
    participant Lamatic as Lamatic Flow
    participant Parser as normalizeResponse

    User->>Page: Fill form & submit
    Page->>Form: Validate input (min 1 activity)
    Form->>Action: Call planTrip(preferences)
    Action->>Lamatic: Dispatch travelPlanner flow
    Note over Lamatic: Multi-agent orchestration<br/>(routing, flights, stays, activities, budget)
    Lamatic-->>Action: Return raw response
    Action->>Parser: normalizeResponse(raw)
    Parser->>Parser: Parse & extract<br/>itinerary, flights, stays, activities, budget, visualizations
    Parser-->>Action: NormalizedTravelPlan
    Action-->>Page: PlanTripResponse (success or error)
    Page->>Page: Update state & render plan details
    Page->>User: Display itinerary, flights, stays, activities, budget, tips, visualizations
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Areas requiring extra attention:

  • actions/orchestrate.ts: Dense parsing and normalization logic with extensive error handling; validate that all response shapes are correctly handled across different Lamatic output formats
  • components/ui/sidebar.tsx: Complex stateful component with cookie persistence, keyboard shortcuts (Cmd/Ctrl + B), mobile-responsive behavior, and nested context; verify state synchronization and mobile drawer interaction edge cases
  • components/ui/carousel.tsx: Embla carousel with keyboard navigation and API exposure; confirm orientation handling and button disabled states work correctly
  • components/ui/chart.tsx: Theme-aware Recharts wrapper with CSS variable injection and config-driven legend/tooltip rendering; ensure color theme variables align across light/dark modes
  • app/page.tsx: Large client component with complex form state, multiple conditional renders, and activity selection limiting; verify form validation, loading states, and error handling pathways
  • Integration flow: Confirm lamaticClient initialization, API key validation, and error propagation from Lamatic through normalization to UI display

Possibly related PRs

  • Fix : Config Removal #42: Modifies the Lamatic client initialization pattern and configuration approach—may have overlapping concerns around lamatic-config.json and LAMATIC_API_KEY environment variable usage.

Poem

🐰 A planner takes form, with components so bright,
Radix UI wrapped, from morning to night,
Lamatic flows dance through multi-agent dreams,
Flights, stays, and activities in travel-planner streams!
With sidebar toggles and charts that align,
This template's a treasure—a hop-skip design!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.39% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add Lamatic agentic travel planner kit" directly and accurately describes the primary purpose of the changeset, which is to introduce a complete, new template directory for a multi-agent travel planning application at templates/agentic/travel-planner/. The title is specific and clear, referencing the key technology (Lamatic), the architectural pattern (agentic), the domain (travel planner), and the artifact type (kit), which collectively convey the essential nature of the change. The title is neither vague nor generic, and it aligns precisely with the PR's stated objective of introducing a multi-agent travel researcher and booking assistant that orchestrates Lamatic agents to generate itineraries and booking guidance.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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: 27

🧹 Nitpick comments (40)
templates/agentic/travel-planner/components/ui/hover-card.tsx (1)

22-42: LGTM!

The content component is properly wrapped in a Portal with sensible defaults and comprehensive animation classes. The Portal ensures the hover card renders in the correct layer, and the extensive Tailwind classes handle all animation states correctly.

Optional: Consider extracting the className for readability.

Line 35 is quite long. If desired, you could extract the base classes to a constant for easier maintenance:

+const hoverCardContentClasses =
+  "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden"
+
 function HoverCardContent({
   className,
   align = "center",
   sideOffset = 4,
   ...props
 }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
   return (
     <HoverCardPrimitive.Portal data-slot="hover-card-portal">
       <HoverCardPrimitive.Content
         data-slot="hover-card-content"
         align={align}
         sideOffset={sideOffset}
-        className={cn(
-          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
-          className
-        )}
+        className={cn(hoverCardContentClasses, className)}
         {...props}
       />
     </HoverCardPrimitive.Portal>
   )
 }

However, this is purely stylistic and the current inline approach is perfectly acceptable.

templates/agentic/travel-planner/components/ui/collapsible.tsx (1)

5-31: Consider simplifying the parameter destructuring pattern.

The rest spread pattern { ...props } in the function parameters (lines 6, 12, 23) is unusual and creates unnecessary object spreading. The standard approach is to use props directly.

For example, refactor the Collapsible component:

-function Collapsible({
-  ...props
-}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
+function Collapsible(
+  props: React.ComponentProps<typeof CollapsiblePrimitive.Root>
+) {
   return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
 }

Apply the same pattern to CollapsibleTrigger and CollapsibleContent.

templates/agentic/travel-planner/components/ui/progress.tsx (1)

25-25: Consider validating the value range.

The transform calculation assumes value is between 0 and 100, but there's no validation. Values outside this range (e.g., 150 or -10) would produce unexpected visual results.

You could clamp the value to ensure it stays within bounds:

-        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
+        style={{ transform: `translateX(-${100 - Math.min(100, Math.max(0, value || 0))}%)` }}
templates/agentic/travel-planner/components/ui/navigation-menu.tsx (2)

76-76: Consider CSS margin instead of text space for icon spacing.

The {" "} pattern adds a text space node between children and the icon. For cleaner markup and better maintainability, consider using a CSS margin class on the icon instead.

Apply this diff:

-      {children}{" "}
+      {children}
       <ChevronDownIcon
-        className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
+        className="relative top-[1px] ml-1.5 size-3 transition duration-300 group-data-[state=open]:rotate-180"
         aria-hidden="true"
       />

85-100: Consider breaking up the long className for readability.

Lines 93-94 contain extremely long className strings that are difficult to read and maintain. While functionally correct, consider extracting repeated prefixes or breaking across multiple lines.

Note: The **:data-[slot=navigation-menu-link] selector is a Tailwind universal descendant selector that targets all descendant elements with the specified data attribute.

Example refactor option:

function NavigationMenuContent({
  className,
  ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
  const baseClasses = "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto"
  
  const viewportFalseClasses = "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none"

  return (
    <NavigationMenuPrimitive.Content
      data-slot="navigation-menu-content"
      className={cn(baseClasses, viewportFalseClasses, className)}
      {...props}
    />
  )
}
templates/agentic/travel-planner/components/ui/drawer.tsx (1)

48-73: <!-- [scratchpad_start]
[task review_file_1/1 verify_drawer_redundancy_and_handle_visibility 3/3]
Status: Verification complete

[observations]
File: ./templates/agentic/travel-planner/components/ui/drawer.tsx (135 lines)

Redundancy on Line 54:

  • DrawerPortal component (lines 21-24) DOES set data-slot="drawer-portal" internally
  • Line 54: passes same attribute as prop
  • This creates duplicate attribute (harmless but unnecessary code noise)
  • Review comment CLAIM VERIFIED AS CORRECT ✓

Handle visibility on Line 68:

  • DrawerContent only shows handle for bottom drawers
  • Component supports all 4 directions with full styling
  • DrawerContent is NOT used anywhere in travel-planner template itself
  • Component is exported as library component for potential external use
  • Cannot verify impact without seeing actual usage in consuming code

[analysis]
Redundancy issue: DEFINITE - should remove data-slot from line 54
Handle visibility: DEBATABLE - component supports all directions, handle only for bottom

  • Could be intentional design
  • Could be incomplete implementation
  • Since component is unused in travel-planner, cannot determine actual usage pattern

[conclusion]

  1. Line 54 redundancy: CONFIRMED - optional refactor to clean up
  2. Line 68 handle visibility: UNVERIFIABLE in this context - component definition supports all directions but is not actually used in travel-planner, so the UX implication cannot be assessed from this template alone

[done]
Ready to generate rewritten review comment with updated findings
</scratchpad_end] -->

Remove redundant data-slot prop; handle visibility design is intentional.

The data-slot="drawer-portal" on line 54 is redundant since DrawerPortal already sets this attribute internally (line 23). Remove the duplicate prop.

Regarding the handle visibility on line 68: the design showing the handle only for bottom drawers appears intentional. The DrawerContent component provides full responsive styling for all four directions (top, bottom, left, right), and the handle visibility can be customized by consumers based on their UX needs. This is acceptable for a reusable UI component.

Apply this diff to clean up the redundancy:

-    <DrawerPortal data-slot="drawer-portal">
+    <DrawerPortal>
templates/agentic/travel-planner/.gitignore (1)

37-37: Add trailing newline.

The file ends without a trailing newline, which violates POSIX standards and may cause issues with some tools.

templates/agentic/travel-planner/components/ui/use-mobile.tsx (1)

5-19: Consider SSR-friendly initialization to prevent hydration mismatches.

The hook initializes isMobile to undefined but returns !!isMobile (false) during SSR and initial client render. This can cause hydration mismatches in Next.js when components render differently based on the mobile state.

Consider one of these approaches:

Option 1: Return undefined initially (preferred for SSR)

-  return !!isMobile
+  return isMobile

Then handle undefined in consuming components with optional chaining or fallback rendering.

Option 2: Initialize based on user agent (SSR-compatible)

-export function useIsMobile() {
-  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
+export function useIsMobile() {
+  const [isMobile, setIsMobile] = React.useState<boolean>(() => {
+    if (typeof window === 'undefined') return false
+    return window.innerWidth < MOBILE_BREAKPOINT
+  })

Option 3: Suppress hydration for mobile-specific content
Use suppressHydrationWarning on elements that differ between mobile/desktop, or wait for isMobile !== undefined before rendering conditional content.

templates/agentic/travel-planner/package.json (1)

47-47: Pin versions instead of using "latest".

Three dependencies use "latest" instead of specific versions, which can lead to non-deterministic builds and potential breaking changes:

  • @vercel/analytics
  • geist
  • react-markdown

Pin these to specific versions to ensure reproducible builds. For example:

-    "@vercel/analytics": "latest",
+    "@vercel/analytics": "^1.4.1",

You can check the latest stable versions with:

#!/bin/bash
# Check latest versions on npm
npm view @vercel/analytics version
npm view geist version
npm view react-markdown version

Also applies to: 54-54, 64-64

templates/agentic/travel-planner/components/ui/switch.tsx (1)

8-29: Consider extracting shared UI components.

This Switch component is duplicated across multiple templates (reasoning, hiring, and now travel-planner). While this works, consider extracting shared UI components to a common location to reduce duplication and improve maintainability.

templates/agentic/travel-planner/components/ui/form.tsx (1)

45-66: Add validation for FormItem context.

The useFormField hook validates that it's used within a FormField (line 52-54) but doesn't validate that it's within a FormItem. Line 56 accesses itemContext.id which will throw a runtime error if the hook is used outside FormItem.

Add validation for itemContext:

   if (!fieldContext) {
     throw new Error("useFormField should be used within <FormField>")
   }
 
+  if (!itemContext) {
+    throw new Error("useFormField should be used within <FormItem>")
+  }
+
   const { id } = itemContext
templates/agentic/travel-planner/tsconfig.json (1)

5-5: Consider updating the TypeScript target.

The ES6 target is from 2015 and may limit access to modern JavaScript features. Modern Next.js projects typically use ES2020 or ESNext for better optimization and language feature support.

Apply this diff to use a more modern target:

-    "target": "ES6",
+    "target": "ES2020",
templates/agentic/travel-planner/components/ui/skeleton.tsx (1)

3-11: Consider extracting shared UI components.

This Skeleton component appears identical to the implementation in templates/agentic/reasoning/components/ui/skeleton.tsx. If multiple templates share the same UI primitives, consider extracting them to a shared component library to improve maintainability and consistency.

templates/agentic/travel-planner/hooks/use-mobile.ts (1)

1-19: Consider consolidating duplicate hook implementations.

The relevant code snippets indicate this hook appears to be duplicated in both hooks/use-mobile.ts and components/ui/use-mobile.tsx. Consider removing the duplicate to maintain a single source of truth.

templates/agentic/travel-planner/app/globals.css (1)

1-125: Consider consolidating duplicated theming across templates.

The theming system defined here appears to be duplicated across multiple templates. Consider extracting the shared theming foundation into a shared utilities package or using a consistent theming configuration file that can be imported across templates.

templates/agentic/travel-planner/app/layout.tsx (1)

23-23: Consider a loading indicator instead of null fallback.

The Suspense component uses fallback={null}, which displays nothing during loading. This could result in a blank screen. Consider using a loading spinner or skeleton to improve perceived performance.

Example:

-<Suspense fallback={null}>{children}</Suspense>
+<Suspense fallback={<div className="flex h-screen items-center justify-center">Loading...</div>}>{children}</Suspense>
templates/agentic/travel-planner/components/ui/separator.tsx (1)

8-26: Component implementation looks good.

The Separator component correctly wraps the Radix UI primitive with appropriate styling and prop forwarding. Note that this component is identical to separators in other templates (reasoning, hiring, embed/chat), which presents an opportunity to consolidate shared UI components.

templates/agentic/travel-planner/components/ui/accordion.tsx (1)

9-13: Forward refs for Radix wrappers; hide decorative icon for a11y.

  • Preserve ref semantics on Root/Item/Trigger/Content; current wrappers drop refs, which can break composition and focus management.
  • Mark Chevron icon as decorative to avoid SR noise.

Apply this refactor:

@@
-function Accordion({
-  ...props
-}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
-  return <AccordionPrimitive.Root data-slot="accordion" {...props} />
-}
+const Accordion = React.forwardRef<
+  React.ElementRef<typeof AccordionPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>
+>(({ ...props }, ref) => (
+  <AccordionPrimitive.Root ref={ref} data-slot="accordion" {...props} />
+))
+Accordion.displayName = "Accordion"
@@
-function AccordionItem({
-  className,
-  ...props
-}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
-  return (
-    <AccordionPrimitive.Item
-      data-slot="accordion-item"
-      className={cn("border-b last:border-b-0", className)}
-      {...props}
-    />
-  )
-}
+const AccordionItem = React.forwardRef<
+  React.ElementRef<typeof AccordionPrimitive.Item>,
+  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
+>(({ className, ...props }, ref) => (
+  <AccordionPrimitive.Item
+    ref={ref}
+    data-slot="accordion-item"
+    className={cn("border-b last:border-b-0", className)}
+    {...props}
+  />
+))
+AccordionItem.displayName = "AccordionItem"
@@
-function AccordionTrigger({
-  className,
-  children,
-  ...props
-}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
-  return (
-    <AccordionPrimitive.Header className="flex">
-      <AccordionPrimitive.Trigger
+const AccordionTrigger = React.forwardRef<
+  React.ElementRef<typeof AccordionPrimitive.Trigger>,
+  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+  <AccordionPrimitive.Header className="flex">
+    <AccordionPrimitive.Trigger
+      ref={ref}
       data-slot="accordion-trigger"
       className={cn(
         "focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
         className
       )}
       {...props}
     >
       {children}
-      <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
-      </AccordionPrimitive.Trigger>
-    </AccordionPrimitive.Header>
-  )
-}
+      <ChevronDownIcon
+        aria-hidden="true"
+        focusable="false"
+        className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200"
+      />
+    </AccordionPrimitive.Trigger>
+  </AccordionPrimitive.Header>
+))
+AccordionTrigger.displayName = "AccordionTrigger"
@@
-function AccordionContent({
-  className,
-  children,
-  ...props
-}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
-  return (
-    <AccordionPrimitive.Content
-      data-slot="accordion-content"
-      className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
-      {...props}
-    >
-      <div className={cn("pt-0 pb-4", className)}>{children}</div>
-    </AccordionPrimitive.Content>
-  )
-}
+const AccordionContent = React.forwardRef<
+  React.ElementRef<typeof AccordionPrimitive.Content>,
+  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
+>(({ className, children, ...props }, ref) => (
+  <AccordionPrimitive.Content
+    ref={ref}
+    data-slot="accordion-content"
+    className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
+    {...props}
+  >
+    <div className={cn("pt-0 pb-4", className)}>{children}</div>
+  </AccordionPrimitive.Content>
+))
+AccordionContent.displayName = "AccordionContent"

Also applies to: 15-26, 28-48, 50-64

templates/agentic/travel-planner/components/ui/calendar.tsx (1)

193-207: Prefer stable ISO date in data attribute.

Locale-dependent toLocaleDateString() varies by user locale; use ISO 8601 for predictable selectors/debugging.

-      data-day={day.date.toLocaleDateString()}
+      data-day={day.date.toISOString().slice(0, 10)}
templates/agentic/travel-planner/components/ui/alert.tsx (1)

6-14: Fix destructive variant descendant selector; current pattern may not compile.

The token *:data-[slot=alert-description]:… isn’t a valid Tailwind arbitrary variant in most setups. Use a descendant arbitrary selector so the description text reliably inherits destructive color.

-        destructive:
-          "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
+        destructive:
+          "text-destructive bg-card [&>svg]:text-current [&_*[data-slot=alert-description]]:text-destructive/90",

If your Tailwind setup doesn’t support nested arbitrary variants, alternatively color the description directly in AlertDescription when variant === "destructive".

templates/agentic/travel-planner/components/ui/use-toast.ts (1)

11-13: Tighten toast ergonomics: effect deps, limits/delays, update typing.

  • Effect depends on state, re-subscribing every change. Use [].
  • TOAST_LIMIT=1 and TOAST_REMOVE_DELAY=1,000,000ms (~17m) are extreme for UX.
  • update() shouldn’t require callers to pass id.
-const TOAST_LIMIT = 1
-const TOAST_REMOVE_DELAY = 1000000
+const TOAST_LIMIT = 3
+const TOAST_REMOVE_DELAY = 5000
@@
-type Toast = Omit<ToasterToast, "id">
+type Toast = Omit<ToasterToast, "id">
@@
-function toast({ ...props }: Toast) {
+function toast({ ...props }: Toast) {
   const id = genId()
 
-  const update = (props: ToasterToast) =>
+  const update = (next: Partial<Toast>) =>
     dispatch({
       type: "UPDATE_TOAST",
-      toast: { ...props, id },
+      toast: { ...next, id },
     })
@@
-  React.useEffect(() => {
+  React.useEffect(() => {
     listeners.push(setState)
     return () => {
       const index = listeners.indexOf(setState)
       if (index > -1) {
         listeners.splice(index, 1)
       }
     }
-  }, [state])
+  }, [])

Also applies to: 174-185, 145-153

templates/agentic/travel-planner/app/page.tsx (3)

172-179: Fix date “today” calculation to local date; avoid UTC off‑by‑one.

toISOString().split("T")[0] uses UTC; users in US timezones can see “today” as tomorrow.

- const today = useMemo(() => new Date().toISOString().split("T")[0], []);
+ const today = useMemo(() => {
+   const d = new Date();
+   const yyyy = d.getFullYear();
+   const mm = String(d.getMonth() + 1).padStart(2, "0");
+   const dd = String(d.getDate()).padStart(2, "0");
+   return `${yyyy}-${mm}-${dd}`; // local YYYY-MM-DD
+ }, []);

No behavior change elsewhere; string compares still work.

Also applies to: 535-563, 571-585


30-31: Remove unused import.

ScrollArea is imported but never used.

-import { ScrollArea } from "@/components/ui/scroll-area";

188-196: Remove unused computed value.

rawOutput is never rendered. Drop it to save work on large payloads.

-  const rawOutput = useMemo(() => {
-    if (!plan) return "";
-    if (typeof plan.raw === "string") return plan.raw;
-    try {
-      return JSON.stringify(plan.raw, null, 2);
-    } catch {
-      return String(plan.raw);
-    }
-  }, [plan]);
templates/agentic/travel-planner/components/ui/tabs.tsx (1)

8-19: Forward refs for Radix wrappers to preserve focus/a11y hooks.

Without forwardRef, consumers can’t attach refs that many Radix patterns rely on.

-import * as React from "react"
+import * as React from "react"

- function Tabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
-   return ( <TabsPrimitive.Root ... /> )
- }
+const Tabs = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.Root>,
+  React.ComponentProps<typeof TabsPrimitive.Root>
+>(({ className, ...props }, ref) => (
+  <TabsPrimitive.Root ref={ref} data-slot="tabs" className={cn("flex flex-col gap-2", className)} {...props} />
+));
+Tabs.displayName = "Tabs";

- function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {
+const TabsList = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.List>,
+  React.ComponentProps<typeof TabsPrimitive.List>
+>(({ className, ...props }, ref) => (
-   <TabsPrimitive.List
+   <TabsPrimitive.List
+      ref={ref}
       data-slot="tabs-list"
       className={cn("bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]", className)}
       {...props}
     />
- }
+));
+TabsList.displayName = "TabsList";

- function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
+const TabsTrigger = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.Trigger>,
+  React.ComponentProps<typeof TabsPrimitive.Trigger>
+>(({ className, ...props }, ref) => (
   <TabsPrimitive.Trigger
+    ref={ref}
     data-slot="tabs-trigger"
     className={cn("data-[state=active]:bg-background ... [&_svg:not([class*='size-'])]:size-4", className)}
     {...props}
   />
- }
+));
+TabsTrigger.displayName = "TabsTrigger";

- function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {
+const TabsContent = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.Content>,
+  React.ComponentProps<typeof TabsPrimitive.Content>
+>(({ className, ...props }, ref) => (
   <TabsPrimitive.Content
+    ref={ref}
     data-slot="tabs-content"
     className={cn("flex-1 outline-none", className)}
     {...props}
   />
- }
+));
+TabsContent.displayName = "TabsContent";

Also applies to: 21-35, 37-51, 53-64

templates/agentic/travel-planner/components/ui/carousel.tsx (1)

78-89: Keyboard UX: honor vertical orientation.

ArrowLeft/ArrowRight are fine for horizontal. For vertical, also handle ArrowUp/ArrowDown.

- if (event.key === "ArrowLeft") { ... } else if (event.key === "ArrowRight") { ... }
+ if (orientation === "vertical") {
+   if (event.key === "ArrowUp") { event.preventDefault(); scrollPrev() }
+   if (event.key === "ArrowDown") { event.preventDefault(); scrollNext() }
+ } else {
+   if (event.key === "ArrowLeft") { event.preventDefault(); scrollPrev() }
+   if (event.key === "ArrowRight") { event.preventDefault(); scrollNext() }
+ }
templates/agentic/travel-planner/components/ui/card.tsx (1)

31-39: Optional: allow semantic headings for accessibility.

CardTitle renders a div. Let consumers choose a heading element.

-function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+function CardTitle({
+  as: As = "div",
+  className,
+  ...props
+}: { as?: "h1"|"h2"|"h3"|"h4"|"h5"|"h6"|"div" } & React.ComponentProps<"div">) {
   return (
-    <div
+    <As
       data-slot="card-title"
       className={cn("leading-none font-semibold", className)}
       {...props}
-    />
+    />
   )
}
templates/agentic/travel-planner/components/ui/input-otp.tsx (1)

9-27: Consider ref forwarding for composition.

Forwarding refs on InputOTP and InputOTPSlot enables focus management and integrations. OTPInput supports refs and can be used with forwardRef, making the suggested refactor technically feasible.

templates/agentic/travel-planner/components/ui/breadcrumb.tsx (1)

34-50: Forward refs for better interop (NextLink, focus mgmt, testing).

Forwarding refs on BreadcrumbLink improves accessibility and composition.

-function BreadcrumbLink({
-  asChild,
-  className,
-  ...props
-}: React.ComponentProps<"a"> & {
-  asChild?: boolean
-}) {
-  const Comp = asChild ? Slot : "a"
-  return (
-    <Comp
-      data-slot="breadcrumb-link"
-      className={cn("hover:text-foreground transition-colors", className)}
-      {...props}
-    />
-  )
-}
+const BreadcrumbLink = React.forwardRef<
+  React.ElementRef<"a">,
+  React.ComponentPropsWithoutRef<"a"> & { asChild?: boolean }
+>(({ asChild, className, ...props }, ref) => {
+  const Comp = asChild ? Slot : "a"
+  return (
+    <Comp
+      ref={ref}
+      data-slot="breadcrumb-link"
+      className={cn("hover:text-foreground transition-colors", className)}
+      {...props}
+    />
+  )
+})
+BreadcrumbLink.displayName = "BreadcrumbLink"
templates/agentic/travel-planner/components/ui/select.tsx (1)

40-42: Replace non-standard selector variants and unify outline class.

The “star” variants (e.g., *:data-[...], *:[span]:last:...) are not Tailwind defaults and likely won’t compile. Use bracketed descendant selectors. Also prefer outline-none over outline-hidden.

-        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 [&_*[data-slot=select-value]]:line-clamp-1 [&_*[data-slot=select-value]]:flex [&_*[data-slot=select-value]]:items-center [&_*[data-slot=select-value]]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
-        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
+        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&>span:last-child]:flex [&>span:last-child]:items-center [&>span:last-child]:gap-2",

Also applies to: 108-113

templates/agentic/travel-planner/components/ui/dialog.tsx (1)

58-60: Small polish: avoid redundant data-slot; use standard outline class.

  • No need to pass data-slot to DialogPortal wrapper (it already sets it).
  • Prefer outline-none over non-standard outline-hidden.
-    <DialogPortal data-slot="dialog-portal">
+    <DialogPortal>
       <DialogOverlay />
       <DialogPrimitive.Content
@@
-          <DialogPrimitive.Close
+          <DialogPrimitive.Close
             data-slot="dialog-close"
-            className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
+            className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
           >

Also applies to: 70-76

templates/agentic/travel-planner/components/ui/menubar.tsx (2)

102-108: Replace non-standard star variant; also use standard outline class.

data-[variant=destructive]:*:[svg]:!text-destructive is not a Tailwind variant. Use a bracketed descendant selector. Also prefer outline-none.

-        "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+        "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:[&_svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",

56-63: Unify focus outline: use outline-none (not outline-hidden).

Non-standard outline-hidden may be dropped by Tailwind; keep consistency with MenubarSubTrigger.

-      className={cn(
-        "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none",
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-none select-none",
-      className={cn(
-        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
-      className={cn(
-        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",

Also applies to: 121-137, 146-151

templates/agentic/travel-planner/components/ui/dropdown-menu.tsx (2)

76-80: Replace non-standard star variant and standardize outline.

Switch to bracketed descendant selector; prefer outline-none.

-        "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+        "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:[&_svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",

94-99: Consistency: use outline-none (not outline-hidden).

Ensure dropdown items/checkbox/radio use standard Tailwind outline utilities.

-      className={cn(
-        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
-      className={cn(
-        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",

Also applies to: 130-134

templates/agentic/travel-planner/components/ui/context-menu.tsx (1)

9-13: Consider forwarding refs for all wrappers

These wrappers don’t use React.forwardRef, so consumers can’t focus/measure elements via refs. Forwarding preserves Radix primitives’ ref behavior.

Example pattern:

const ContextMenuItem = React.forwardRef<
  React.ElementRef<typeof ContextMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & { inset?: boolean; variant?: "default" | "destructive" }
>(({ className, inset, variant = "default", ...props }, ref) => (
  <ContextMenuPrimitive.Item ref={ref} ... />
));
ContextMenuItem.displayName = "ContextMenuItem";
templates/agentic/travel-planner/actions/orchestrate.ts (2)

9-11: Avoid import-time throw; guard inside planTrip

Throwing at module load breaks any import (including build/SSR). Prefer a runtime guard that returns a structured error.

Apply:

-if (!travelPlannerFlow) {
-  throw new Error("travelPlanner flow not found in lamatic-config.json");
-}

And add near the start of planTrip:

 export async function planTrip(
   preferences: TravelPreferences
 ): Promise<PlanTripResponse> {
+  if (!travelPlannerFlow) {
+    return { success: false, error: "travelPlanner flow not configured." };
+  }
   try {

803-816: Minor: normalize/validate input early

Trim strings and validate date order; clamp negative budgets/travelers.

Example:

if (new Date(preferences.startDate) > new Date(preferences.endDate)) {
  return { success: false, error: "startDate must be before endDate." };
}
const budget = Number.isFinite(preferences.budget) && preferences.budget >= 0 ? preferences.budget : undefined;
templates/agentic/travel-planner/components/ui/sidebar.tsx (2)

85-90: Harden and persist cookie correctly (samesite, secure; hydrate on mount)

Current cookie lacks samesite/secure and the persisted state isn’t read back.

Apply:

-      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
+      const secure = window.location.protocol === "https:" ? "; secure" : ""
+      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}; samesite=lax${secure}`

Hydrate on mount when uncontrolled:

   const [_open, _setOpen] = React.useState(defaultOpen)
   const open = openProp ?? _open
+  React.useEffect(() => {
+    if (openProp !== undefined) return
+    const m = document.cookie.match(new RegExp(`(?:^|; )${SIDEBAR_COOKIE_NAME}=([^;]*)`))
+    if (m) _setOpen(m[1] === "true")
+  }, [openProp])

282-304: Optional a11y: SidebarRail is unfocusable

tabIndex={-1} prevents keyboard access. Consider leaving it unfocusable (pure pointer affordance) or add a hidden label + keyboard toggle alternative.

You already provide SidebarTrigger; ensure parity for keyboard users is documented/available.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 06d0dfb and 6ef77b7.

⛔ Files ignored due to path filters (3)
  • templates/agentic/travel-planner/app/icon.png is excluded by !**/*.png
  • templates/agentic/travel-planner/public/lamatic-logo.png is excluded by !**/*.png
  • templates/agentic/travel-planner/public/placeholder.svg is excluded by !**/*.svg
📒 Files selected for processing (73)
  • templates/agentic/travel-planner/.env.example (1 hunks)
  • templates/agentic/travel-planner/.eslintrc.json (1 hunks)
  • templates/agentic/travel-planner/.gitignore (1 hunks)
  • templates/agentic/travel-planner/.npmrc (1 hunks)
  • templates/agentic/travel-planner/LICENSE (1 hunks)
  • templates/agentic/travel-planner/README.md (1 hunks)
  • templates/agentic/travel-planner/actions/orchestrate.ts (1 hunks)
  • templates/agentic/travel-planner/app/globals.css (1 hunks)
  • templates/agentic/travel-planner/app/layout.tsx (1 hunks)
  • templates/agentic/travel-planner/app/page.tsx (1 hunks)
  • templates/agentic/travel-planner/components.json (1 hunks)
  • templates/agentic/travel-planner/components/theme-provider.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/accordion.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/alert-dialog.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/alert.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/aspect-ratio.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/avatar.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/badge.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/breadcrumb.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/button.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/calendar.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/card.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/carousel.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/chart.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/checkbox.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/collapsible.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/command.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/context-menu.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/dialog.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/drawer.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/dropdown-menu.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/form.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/hover-card.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/input-otp.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/input.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/label.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/menubar.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/navigation-menu.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/pagination.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/popover.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/progress.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/radio-group.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/resizable.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/scroll-area.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/select.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/separator.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/sheet.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/sidebar.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/skeleton.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/slider.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/sonner.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/switch.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/table.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/tabs.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/textarea.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/toast.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/toaster.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/toggle-group.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/toggle.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/tooltip.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/use-mobile.tsx (1 hunks)
  • templates/agentic/travel-planner/components/ui/use-toast.ts (1 hunks)
  • templates/agentic/travel-planner/hooks/use-mobile.ts (1 hunks)
  • templates/agentic/travel-planner/hooks/use-toast.ts (1 hunks)
  • templates/agentic/travel-planner/lamatic-config.json (1 hunks)
  • templates/agentic/travel-planner/lib/lamatic-client.ts (1 hunks)
  • templates/agentic/travel-planner/lib/utils.ts (1 hunks)
  • templates/agentic/travel-planner/next.config.mjs (1 hunks)
  • templates/agentic/travel-planner/package.json (1 hunks)
  • templates/agentic/travel-planner/postcss.config.mjs (1 hunks)
  • templates/agentic/travel-planner/scripts/deploy.sh (1 hunks)
  • templates/agentic/travel-planner/styles/globals.css (1 hunks)
  • templates/agentic/travel-planner/tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (23)
templates/agentic/travel-planner/components/theme-provider.tsx (2)
templates/embed/chat/components/theme-provider.tsx (1)
  • ThemeProvider (9-11)
templates/agentic/reasoning/components/theme-provider.tsx (1)
  • ThemeProvider (9-11)
templates/agentic/travel-planner/components/ui/toaster.tsx (4)
templates/agentic/travel-planner/components/ui/sonner.tsx (1)
  • Toaster (25-25)
templates/agentic/travel-planner/components/ui/use-toast.ts (1)
  • useToast (194-194)
templates/agentic/travel-planner/hooks/use-toast.ts (1)
  • useToast (194-194)
templates/agentic/travel-planner/components/ui/toast.tsx (6)
  • ToastProvider (122-122)
  • Toast (124-124)
  • ToastTitle (125-125)
  • ToastDescription (126-126)
  • ToastClose (127-127)
  • ToastViewport (123-123)
templates/agentic/travel-planner/components/ui/skeleton.tsx (1)
templates/agentic/reasoning/components/ui/skeleton.tsx (1)
  • Skeleton (3-11)
templates/agentic/travel-planner/components/ui/textarea.tsx (3)
templates/agentic/reasoning/components/ui/textarea.tsx (1)
  • Textarea (5-16)
templates/automation/hiring/components/ui/textarea.tsx (1)
  • Textarea (5-16)
templates/embed/chat/components/ui/textarea.tsx (1)
  • Textarea (5-16)
templates/agentic/travel-planner/components/ui/sonner.tsx (2)
templates/agentic/travel-planner/components/ui/toaster.tsx (1)
  • Toaster (13-35)
templates/agentic/reasoning/components/ui/sonner.tsx (1)
  • props (6-23)
templates/agentic/travel-planner/components/ui/badge.tsx (2)
templates/agentic/reasoning/components/ui/badge.tsx (1)
  • Badge (28-44)
templates/automation/hiring/components/ui/badge.tsx (1)
  • Badge (28-44)
templates/agentic/travel-planner/components/ui/switch.tsx (2)
templates/agentic/reasoning/components/ui/switch.tsx (1)
  • Switch (8-29)
templates/automation/hiring/components/ui/switch.tsx (1)
  • Switch (8-29)
templates/agentic/travel-planner/components/ui/separator.tsx (3)
templates/agentic/reasoning/components/ui/separator.tsx (1)
  • Separator (8-26)
templates/automation/hiring/components/ui/separator.tsx (1)
  • Separator (8-26)
templates/embed/chat/components/ui/separator.tsx (1)
  • Separator (8-26)
templates/agentic/travel-planner/components/ui/avatar.tsx (1)
templates/agentic/reasoning/components/ui/avatar.tsx (1)
  • Avatar (8-22)
templates/agentic/travel-planner/components/ui/command.tsx (1)
templates/agentic/travel-planner/components/ui/dialog.tsx (5)
  • Dialog (133-133)
  • DialogHeader (138-138)
  • DialogTitle (141-141)
  • DialogDescription (136-136)
  • DialogContent (135-135)
templates/agentic/travel-planner/components/ui/scroll-area.tsx (1)
templates/agentic/reasoning/components/ui/scroll-area.tsx (1)
  • ScrollArea (8-29)
templates/agentic/travel-planner/hooks/use-mobile.ts (1)
templates/agentic/travel-planner/components/ui/use-mobile.tsx (1)
  • useIsMobile (5-19)
templates/agentic/travel-planner/components/ui/calendar.tsx (1)
templates/agentic/reasoning/components/ui/calendar.tsx (1)
  • Calendar (14-173)
templates/agentic/travel-planner/components/ui/toggle.tsx (1)
templates/agentic/reasoning/components/ui/toggle.tsx (1)
  • Toggle (31-45)
templates/agentic/travel-planner/hooks/use-toast.ts (1)
templates/agentic/travel-planner/components/ui/toast.tsx (3)
  • ToastProps (120-120)
  • ToastActionElement (121-121)
  • Toast (124-124)
templates/agentic/travel-planner/components/ui/use-toast.ts (1)
templates/agentic/travel-planner/components/ui/toast.tsx (3)
  • ToastProps (120-120)
  • ToastActionElement (121-121)
  • Toast (124-124)
templates/agentic/travel-planner/app/page.tsx (1)
templates/agentic/travel-planner/actions/orchestrate.ts (3)
  • NormalizedTravelPlan (124-141)
  • planTrip (793-852)
  • FlightLeg (113-117)
templates/agentic/travel-planner/components/ui/alert-dialog.tsx (1)
templates/agentic/travel-planner/components/ui/button.tsx (1)
  • buttonVariants (59-59)
templates/agentic/travel-planner/components/ui/chart.tsx (1)
templates/agentic/travel-planner/postcss.config.mjs (1)
  • config (2-6)
templates/agentic/travel-planner/components/ui/button.tsx (2)
templates/embed/sheets/components/ui/button.tsx (1)
  • Button (39-58)
templates/embed/chat/components/ui/button.tsx (1)
  • Button (38-57)
templates/agentic/travel-planner/components/ui/pagination.tsx (1)
templates/agentic/reasoning/components/ui/pagination.tsx (1)
  • Pagination (11-21)
templates/agentic/travel-planner/actions/orchestrate.ts (1)
templates/agentic/travel-planner/lib/lamatic-client.ts (1)
  • lamaticClient (10-14)
templates/agentic/travel-planner/components/ui/sidebar.tsx (1)
templates/agentic/travel-planner/hooks/use-mobile.ts (1)
  • useIsMobile (5-19)
🪛 ast-grep (0.39.6)
templates/agentic/travel-planner/app/page.tsx

[warning] 1422-1422: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

templates/agentic/travel-planner/components/ui/chart.tsx

[warning] 82-82: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
templates/agentic/travel-planner/app/page.tsx

[error] 1423-1424: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

templates/agentic/travel-planner/components/ui/chart.tsx

[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

Comment on lines +30 to +34
# package manager
package-lock.json
yarn.lock
pnpm-lock.yaml
bun.lockb
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

Do not ignore package manager lockfiles.

Ignoring lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb) breaks build reproducibility. Lockfiles ensure consistent dependency versions across development, CI/CD, and production environments. Without them, different environments may resolve to different package versions, leading to "works on my machine" issues and potential bugs in production.

Apply this diff to commit lockfiles:

 # package manager
-package-lock.json
-yarn.lock
-pnpm-lock.yaml
-bun.lockb

After removing these entries, commit the appropriate lockfile(s) for the package manager(s) you're using.

📝 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
# package manager
package-lock.json
yarn.lock
pnpm-lock.yaml
bun.lockb
# package manager
🤖 Prompt for AI Agents
templates/agentic/travel-planner/.gitignore lines 30-34 currently ignore package
manager lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb);
remove those entries from .gitignore and commit the relevant lockfile(s) for the
package manager(s) you use (e.g., commit package-lock.json for npm, yarn.lock
for Yarn, etc.) so dependency resolution is reproducible across environments.

@@ -0,0 +1 @@
force=true No newline at end of file
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

Remove force=true to prevent unsafe dependency installations.

The force=true flag bypasses npm's safety checks and conflict resolution, which can lead to corrupted node_modules, silently ignored dependency conflicts, and difficult-to-debug production issues. Dependency conflicts should be resolved explicitly in package.json rather than forcing installations.

Apply this diff to remove the unsafe configuration:

-force=true

Alternatively, if there are specific dependency conflicts, address them explicitly in package.json with resolutions or overrides.

📝 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
force=true
🤖 Prompt for AI Agents
templates/agentic/travel-planner/.npmrc lines 1-1: remove the unsafe
"force=true" setting from .npmrc (or change it to "force=false") so npm no
longer bypasses safety checks; instead resolve any dependency conflicts
explicitly in package.json using "resolutions" or "overrides" or by updating
package versions, then re-run install to verify the lockfile and node_modules
are consistent.

Comment on lines +124 to +141
export interface NormalizedTravelPlan {
overview?: string;
keyHighlights: string[];
itinerary: TravelDayPlan[];
flights: FlightOption[];
stays: StayOption[];
activities: ActivityOption[];
budget?: BudgetSummary;
visualizations: VisualizationResource[];
bookingTasks: BookingTask[];
references: string[];
travelTips: string[];
nextSteps: string[];
tripSummary?: TripSummary;
flightItinerary?: FlightItinerary;
accommodation?: StayOption;
raw: unknown;
}
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

Don’t expose raw provider response to clients by default

NormalizedTravelPlan.raw may contain unintended data. Gate it to dev or make it optional and strip before returning.

Apply:

-export interface NormalizedTravelPlan {
+export interface NormalizedTravelPlan {
   ...
-  raw: unknown;
+  raw?: unknown;
 }

And sanitize in planTrip:

-    const plan = normalizeTravelPlan(rawResult);
-
-    return {
+    const plan = normalizeTravelPlan(rawResult);
+    const safePlan: NormalizedTravelPlan =
+      process.env.NODE_ENV === "development" ? plan : { ...plan, raw: undefined };
+    return {
       success: true,
       status,
-      plan,
+      plan: safePlan,
     };

Also applies to: 830-836

🤖 Prompt for AI Agents
In templates/agentic/travel-planner/actions/orchestrate.ts around lines 124–141
(and also address related usage at lines 830–836), the NormalizedTravelPlan
currently exposes a raw provider response; make the property optional (raw?:
unknown) and ensure planTrip does not return raw to clients by default — only
attach raw when running in a development environment (e.g., NODE_ENV ===
"development") or when an explicit debug flag is passed; additionally,
sanitize/strip the raw property before sending the plan response (remove or
clone the object without raw), and update any code at lines 830–836 that
constructs or returns the plan to follow this rule so production responses never
include raw provider data.

Comment on lines +21 to +22
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
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 | 🔴 Critical

Fix duplicate color value for destructive foreground.

Lines 21-22 assign the same color value oklch(0.577 0.245 27.325) to both --destructive and --destructive-foreground. This would result in red text on a red background, making destructive messages unreadable. The dark mode theme (lines 57-58) correctly uses different values.

Apply this diff to fix the foreground color:

   --destructive: oklch(0.577 0.245 27.325);
-  --destructive-foreground: oklch(0.577 0.245 27.325);
+  --destructive-foreground: oklch(0.985 0 0);
📝 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
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/app/globals.css around lines 21-22, the CSS
custom property --destructive and --destructive-foreground are set to the same
oklch value causing unreadable red-on-red text; change --destructive-foreground
to a contrasting (lighter) foreground color (use the same foreground value
pattern as the dark-mode block at lines ~57-58) so destructive text remains
readable against the destructive background.

Comment on lines +1421 to +1426
<div
className="aspect-video overflow-hidden rounded-md border"
dangerouslySetInnerHTML={{
__html: viz.embedHtml,
}}
/>
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

XSS risk: sanitize HTML before using dangerouslySetInnerHTML.

viz.embedHtml is untrusted; injects raw HTML into the DOM.

Apply a sanitizer (DOMPurify or isomorphic-dompurify) before rendering.

+import DOMPurify from "isomorphic-dompurify";
...
 {viz.embedHtml && (
   <div
     className="aspect-video overflow-hidden rounded-md border"
     dangerouslySetInnerHTML={{
-      __html: viz.embedHtml,
+      __html: DOMPurify.sanitize(viz.embedHtml, { ADD_ATTR: ["target","rel"] }),
     }}
   />
 )}

Based on static analysis hints.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 ast-grep (0.39.6)

[warning] 1422-1422: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)

[error] 1423-1424: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

Comment on lines +11 to +12
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
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

Review the toast removal delay configuration.

The TOAST_REMOVE_DELAY is set to 1,000,000ms (~16.7 minutes), which is unusually long for toast notifications. Typical toast removal delays range from 3-10 seconds. This extended delay may result in excessive memory usage as dismissed toasts remain in the removal queue for an extended period.

Consider reducing the delay to a more typical value:

 const TOAST_LIMIT = 1
-const TOAST_REMOVE_DELAY = 1000000
+const TOAST_REMOVE_DELAY = 5000
📝 Committable suggestion

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

Suggested change
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 5000
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/hooks/use-toast.ts around lines 11 to 12,
the TOAST_REMOVE_DELAY is set to 1,000,000ms which is far too long for toast
notifications; change the constant to a typical short duration (e.g., 3000–7000
ms, recommended 5000 ms) so toasts are removed promptly to avoid memory buildup,
and keep TOAST_LIMIT as-is or adjust if needed.

Comment on lines +177 to +185
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
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

Remove state from the useEffect dependency array.

Including state in the dependency array causes the listener to be re-registered on every state update, leading to unnecessary re-renders and potential memory issues. The listener should only be registered once on mount and cleaned up on unmount.

Apply this diff:

   React.useEffect(() => {
     listeners.push(setState)
     return () => {
       const index = listeners.indexOf(setState)
       if (index > -1) {
         listeners.splice(index, 1)
       }
     }
-  }, [state])
+  }, [])
📝 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
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [])
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/hooks/use-toast.ts around lines 177 to 185,
the useEffect currently lists `state` as a dependency which causes the listener
to be re-registered on every state change; remove `state` from the dependency
array so the effect runs only once on mount and cleans up on unmount (i.e.,
replace `[state]` with `[]`), ensuring the listener is registered a single time
and spliced out in the cleanup.

Comment on lines +4 to +14
if (!process.env.LAMATIC_API_KEY) {
throw new Error(
"LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
);
}

export const lamaticClient = new Lamatic({
endpoint: config.api.endpoint,
projectId: config.api.projectId,
apiKey: process.env.LAMATIC_API_KEY,
});
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 | 🔴 Critical

Critical: Module initialization prevents building without API key.

The environment variable check and client instantiation occur at module initialization (lines 4-14). In Next.js, when this module is imported by server components or actions, it's evaluated at build time. This means:

  1. The build will fail if LAMATIC_API_KEY is not set during build
  2. You cannot build once and deploy with different environment variables
  3. This breaks standard CI/CD patterns where you build in one environment and deploy to multiple environments with different credentials

Refactor to use lazy initialization:

 import { Lamatic } from "lamatic";
 import config from "@/lamatic-config.json";

-if (!process.env.LAMATIC_API_KEY) {
-  throw new Error(
-    "LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
-  );
-}
-
-export const lamaticClient = new Lamatic({
-  endpoint: config.api.endpoint,
-  projectId: config.api.projectId,
-  apiKey: process.env.LAMATIC_API_KEY,
-});
+let lamaticClient: Lamatic | null = null;
+
+export function getLamaticClient(): Lamatic {
+  if (!lamaticClient) {
+    if (!process.env.LAMATIC_API_KEY) {
+      throw new Error(
+        "LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
+      );
+    }
+    lamaticClient = new Lamatic({
+      endpoint: config.api.endpoint,
+      projectId: config.api.projectId,
+      apiKey: process.env.LAMATIC_API_KEY,
+    });
+  }
+  return lamaticClient;
+}

Then update imports to use getLamaticClient() instead of lamaticClient.

📝 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
if (!process.env.LAMATIC_API_KEY) {
throw new Error(
"LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
);
}
export const lamaticClient = new Lamatic({
endpoint: config.api.endpoint,
projectId: config.api.projectId,
apiKey: process.env.LAMATIC_API_KEY,
});
import { Lamatic } from "lamatic";
import config from "@/lamatic-config.json";
let lamaticClient: Lamatic | null = null;
export function getLamaticClient(): Lamatic {
if (!lamaticClient) {
if (!process.env.LAMATIC_API_KEY) {
throw new Error(
"LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
);
}
lamaticClient = new Lamatic({
endpoint: config.api.endpoint,
projectId: config.api.projectId,
apiKey: process.env.LAMATIC_API_KEY,
});
}
return lamaticClient;
}
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/lib/lamatic-client.ts around lines 4 to 14,
the current module-level env check and new Lamatic(...) call causes the module
to throw at build-time if LAMATIC_API_KEY is missing; replace this with a lazy
initializer: export a getLamaticClient() function that reads
process.env.LAMATIC_API_KEY at call time, throws only when actually invoked
without a key (not at module import), and memoizes/returns a single Lamatic
instance (or accepts an optional apiKey param) so imports use getLamaticClient()
instead of a module-level lamaticClient; update callers to call
getLamaticClient() where needed.

"url": "https://github.com/Lamatic/AgentKit"
},
"version": "0.1.0",
"private": false,
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 | 🔴 Critical

🧩 Analysis chain

Verify if the package should be public.

The package is marked as "private": false, which means it could be published to npm. For a template within the AgentKit repository, this should typically be "private": true to prevent accidental publication.

Apply this diff if the package should remain private:

-  "private": false,
+  "private": true,

I'll verify this review comment by examining the repository context and comparing with similar files.


🏁 Script executed:

#!/bin/bash
# Find all package.json files in templates directory and check "private" field
echo "=== Checking package.json files in templates directory ==="
find templates -name "package.json" -type f | head -20 | while read file; do
  echo "File: $file"
  jq -r '.private // "not set"' "$file" 2>/dev/null || echo "  (parsing error)"
done

echo -e "\n=== Specifically checking agentic/travel-planner/package.json ==="
if [ -f "templates/agentic/travel-planner/package.json" ]; then
  jq '.private, .name, .description' templates/agentic/travel-planner/package.json 2>/dev/null
else
  echo "File not found"
fi

echo -e "\n=== Checking for publishConfig or other publish-related fields ==="
find templates -name "package.json" -type f | head -10 | xargs jq -r 'select(.publishConfig != null) | .name + ": " + (.publishConfig | tostring)' 2>/dev/null

Length of output: 721


🏁 Script executed:

#!/bin/bash
# Check repository root for publishing guidelines and CI workflows
echo "=== Checking for publishing documentation ==="
ls -la README* PUBLISHING* .npmrc .npmignore 2>/dev/null | head -10

echo -e "\n=== Checking for publish workflows in .github/workflows ==="
find .github/workflows -name "*.yml" -o -name "*.yaml" 2>/dev/null | xargs grep -l "publish\|npm publish" 2>/dev/null || echo "No publish workflows found"

echo -e "\n=== Checking root package.json for workspace config ==="
jq '.private, .workspaces, .publishConfig' package.json 2>/dev/null || echo "No root package.json or error parsing"

Length of output: 287


🏁 Script executed:

#!/bin/bash
# Check README for information about templates and publishing intent
echo "=== README.md content (first 100 lines) ==="
head -100 README.md

echo -e "\n=== Searching for template-related guidance in README ==="
grep -i -A5 -B5 "template\|private\|publish\|package.json" README.md | head -50

Length of output: 10618


Set "private": true to align with template convention.

The travel-planner template is marked as "private": false, making it publishable to npm. This is inconsistent with other templates in the repository (automation/hiring, embed/*, special/halloween-costume) which set "private": true. Templates are starter kits meant for individual cloning and deployment, not separate npm packages.

-  "private": false,
+  "private": true,
📝 Committable suggestion

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

Suggested change
"private": false,
"private": true,
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/package.json around line 10, the package is
incorrectly marked "private": false; update this to "private": true to match
repository template convention and prevent accidental npm publishing; ensure the
JSON remains valid (keep trailing comma rules consistent with surrounding
entries) and run a quick lint/format check after the change.

Comment on lines +1 to +6
#!/bin/bash

npm install

# Run the dev server
npm run dev No newline at end of file
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

Rename script and add error handling.

The script name "deploy.sh" is misleading since it runs the development server (npm run dev) rather than performing an actual deployment. Additionally, the script lacks error handling—if npm install fails, npm run dev will still execute.

Apply this diff to improve the script:

 #!/bin/bash
+set -e  # Exit on error
 
 npm install
 
 # Run the dev server
 npm run dev

Consider renaming the file to dev.sh or start-dev.sh to better reflect its purpose.

📝 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
#!/bin/bash
npm install
# Run the dev server
npm run dev
#!/bin/bash
set -e # Exit on error
npm install
# Run the dev server
npm run dev
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/scripts/deploy.sh (lines 1-6): the script
name is misleading and it lacks error handling so `npm run dev` will run even if
`npm install` fails; rename the file to dev.sh or start-dev.sh to reflect its
purpose, make the script fail fast by adding `set -e` (or explicitly check the
exit status of `npm install` and exit with a non-zero code on failure), and keep
the script content to run `npm install` then `npm run dev` only if install
succeeds; also ensure the new file is executable (chmod +x).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant