Skip to content

Conversation

@AasheeshLikePanner
Copy link

@AasheeshLikePanner AasheeshLikePanner commented Dec 23, 2025

Blog Writing Automation AgentKit

What This Asset Does

The Blog Writing Automation AgentKit is an end-to-end AI-powered orchestration tool designed to automate the lifecycle of content creation and publishing. It solves the production of consistent blog content by following a high-quality pipeline:

  1. Triggering: External webhooks (from CRM, Zapier, etc.) or scheduled tasks signal the start.
  2. Extraction: Topic, keywords, and stylistic instructions are fetched from the trigger payload.
  3. Intelligent Drafting: AI generates initial content with a focus on SEO and technical coherence.
  4. Optimization: A secondary pass refines the content for target search terms and readability.
  5. Multi-Platform Publishing: The final post is published automatically to a CMS (e.g., WordPress, Ghost) or static platform via API.

The kit provides a premium glassmorphic dashboard for manual triggering and status monitoring.

Providers & Prerequisites

  • Lamatic.ai: Core orchestration and workflow execution engine.
  • Lamatic GraphQL Endpoint: Project-specific endpoint for high-performance interaction.
  • Project IDs & API Keys: Required for secure authentication.

How to Run Locally (only for Kits)

  1. Navigate to /kits/automation/blog-automation.
  2. Install with bun install or npm install.
  3. Create a .env file using .env.example as a template.
  4. Run development server: npm run dev.

Issue: #27

Demo

Screen.Recording.2025-12-23.at.11.29.10.PM.1.mp4

Lamatic Flow

Flow configurations and schemas are maintained in the /flows directory of the kit.


PR Checklist [Kit]

  • Kit runs locally with npm run dev
  • .env contains all required keys (no secrets in commits)
  • .env.example has no secrets, only placeholders
  • README.md documents setup, environment variables, and usage
  • Folder structure: kits/automation/blog-automation/
  • config.json and orchestrate.js are present and valid
  • All flows exported in flows/ folder
  • Vercel deployment works with environment variables set
  • Live preview URL works end-to-end

Summary by CodeRabbit

  • New Features

    • Blog Writing Automation kit with UI to submit topics and get AI-drafted, SEO‑optimized content and publishable posts (includes mock mode).
    • In-app results view with copy Markdown and "View Live Post" when published.
    • Themed UI, layout, header, and global styling for a polished web experience.
  • Configuration

    • Environment template, README, and project config added to simplify setup.
  • Chores

    • .gitignore and build tooling configs included.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

📝 Walkthrough

Walkthrough

Adds a new Blog Writing Automation kit: a Next.js app, Lamatic orchestration (draft → SEO → publish), UI pages and components, flow definitions and configs, Lamatic client/utilities, theme and styling, and shadcn UI component library scaffolding for the kit.

Changes

Cohort / File(s) Summary
Project config & toolchain
kits/automation/blog-automation/.env.example, .../.gitignore, kits/automation/blog-automation/package.json, kits/automation/blog-automation/tsconfig.json, kits/automation/blog-automation/next.config.mjs, kits/automation/blog-automation/postcss.config.mjs
New env template, gitignore, package metadata, TypeScript/Next/PostCSS configuration and build settings.
README & docs
kits/automation/blog-automation/README.md, flows/blog-drafting/README.md, flows/blog-seo/README.md, flows/blog-publish/README.md
Project and per-flow documentation added describing architecture, usage, inputs/outputs, and setup steps.
Orchestration & config
kits/automation/blog-automation/actions/orchestrate.ts, kits/automation/blog-automation/orchestrate.ts, kits/automation/blog-automation/config.json, kits/automation/blog-automation/lib/lamatic-client.ts, kits/automation/blog-automation/.env.example
New server-side runBlogAutomation orchestration, typed BlogAutomationResult, Lamatic config model, client initializer, and env placeholders for flow/workspace creds. Mock mode and detailed error handling included.
App layout & page
kits/automation/blog-automation/app/layout.tsx, kits/automation/blog-automation/app/page.tsx, kits/automation/blog-automation/app/globals.css
Next.js RootLayout, global Tailwind CSS theme variables, fonts, analytics, and client page UI to run the automation and display results.
Theme & header
kits/automation/blog-automation/components/theme-provider.tsx, kits/automation/blog-automation/components/header.tsx
Theme provider wrapper and site header component.
UI scaffold config
kits/automation/blog-automation/components.json
shadcn UI configuration and aliases for component generation.
UI component library
kits/automation/blog-automation/components/ui/* (many files)
Large set of new UI primitives (accordion, alert, badge, button, card, carousel, chart, command, dialog, dropdowns, form primitives, inputs, menus, popover, progress, sidebar, table, tabs, toast, tooltip, and many utilities). Each wraps primitives (Radix/CmdK/Embla/etc.) with consistent styling, data-slot attributes, and exports.
Flows: drafting / seo / publish
kits/automation/blog-automation/flows/blog-drafting/*, .../blog-seo/*, .../blog-publish/*
New flow config, inputs.json, meta.json and README for three sequential flows (drafting → SEO optimization → CMS publishing) describing nodes, edges, and schemas.
Hooks & toast system
kits/automation/blog-automation/hooks/use-mobile.ts, kits/automation/blog-automation/hooks/use-toast.ts, kits/automation/blog-automation/components/ui/use-toast.ts
useIsMobile hook, in-memory toast manager and consumer components/hooks for notifications.
Utilities
kits/automation/blog-automation/lib/utils.ts
Utility cn function (clsx + twMerge) for class name merging.
Styling & assets
kits/automation/blog-automation/app/globals.css, various components
Tailwind theme tokens, dark mode overrides, base layers, and animation utilities.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Client as Browser UI<br/>(page.tsx)
    participant Server as Next.js Server<br/>(actions/orchestrate.ts)
    participant Lamatic as Lamatic API
    participant CMS as CMS (WordPress)

    User->>Client: Submit topic, keywords, instructions
    Client->>Server: POST runBlogAutomation(topic, keywords, instructions)
    activate Server

    rect rgba(70,130,180,0.08)
    Note over Server: Phase 1 — Drafting
    Server->>Lamatic: Execute drafting flow (flowId drafting)
    Lamatic-->>Server: draftContent
    end

    rect rgba(60,179,113,0.08)
    Note over Server: Phase 2 — SEO Optimization
    Server->>Lamatic: Execute SEO flow (draft → optimize)
    Lamatic-->>Server: optimizedContent
    end

    rect rgba(255,165,0,0.08)
    Note over Server: Phase 3 — Publishing
    Server->>Lamatic: Execute publish flow (optimizedContent → CMS call)
    Lamatic->>CMS: POST to WordPress (API)
    CMS-->>Lamatic: publish response (url, status, id)
    Lamatic-->>Server: publishResult
    end

    Server-->>Client: BlogAutomationResult { success, draft, optimizedContent, url, error? }
    deactivate Server
    Client->>User: Render results, show publish URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Fix : Config Removal #42: Updates Lamatic client configuration approach which may affect lamatic-client initialization and environment handling in this kit.

Poem

🐰
I hopped through code to stitch the flows,
Drafting, SEO, then where publishing goes.
Buttons and themes in tidy rows,
A tiny kit that nimbly grows.
Hooray — the blog blooms, and the rabbit knows!

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 title 'feat: implement premium Blog Writing Automation kit with multi-step orchestration' clearly describes the primary change: a new blog automation kit with orchestration pipeline. It is specific, action-oriented, and summarizes the main contribution without being vague or misleading.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: defaults

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 1516d11 and 22e7262.

📒 Files selected for processing (2)
  • kits/automation/blog-automation/actions/orchestrate.ts
  • kits/automation/blog-automation/next.config.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • kits/automation/blog-automation/next.config.mjs
  • kits/automation/blog-automation/actions/orchestrate.ts

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

@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: 14

Note

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

♻️ Duplicate comments (1)
kits/automation/blog-automation/components/ui/use-toast.ts (1)

1-191: Duplicate file - consolidate with hooks/use-toast.ts.

This file is a duplicate of kits/automation/blog-automation/hooks/use-toast.ts. Consider consolidating to a single location and re-exporting if needed from the other path.

🟡 Minor comments (14)
kits/automation/blog-automation/components/ui/carousel.tsx-96-105 (1)

96-105: Missing cleanup for reInit event listener.

The effect subscribes to both reInit and select events but only removes the select listener on cleanup. This causes event handler accumulation if the component re-renders with a new api or onSelect reference.

🔎 Proposed fix
   React.useEffect(() => {
     if (!api) return
     onSelect(api)
     api.on('reInit', onSelect)
     api.on('select', onSelect)

     return () => {
+      api?.off('reInit', onSelect)
       api?.off('select', onSelect)
     }
   }, [api, onSelect])
kits/automation/blog-automation/components/ui/chart.tsx-235-239 (1)

235-239: Zero values will not be displayed due to falsy check.

The condition item.value && treats 0 as falsy, so legitimate zero values won't render in the tooltip. This could hide important data points.

🔎 Proposed fix
-                    {item.value && (
+                    {item.value !== undefined && item.value !== null && (
                       <span className="text-foreground font-mono font-medium tabular-nums">
                         {item.value.toLocaleString()}
                       </span>
                     )}

Alternatively, if you want to show zeros explicitly:

-                    {item.value && (
+                    {item.value != null && (
kits/automation/blog-automation/components/ui/collapsible.tsx-3-3 (1)

3-3: Update @radix-ui/react-collapsible from 1.1.2 to 1.1.12.

The dependency is secure with no known vulnerabilities, but is 11 patch versions behind the latest stable release. Update to 1.1.12 to benefit from bug fixes and improvements.

kits/automation/blog-automation/components/ui/slider.tsx-16-24 (1)

16-24: Verify the default thumb count behavior.

The _values fallback of [min, max] creates a two-thumb range slider by default when neither value nor defaultValue are provided. This may not align with typical user expectations for a slider component, which usually defaults to a single thumb.

Consider changing the fallback to [min] for a single-thumb slider, or document this two-thumb default behavior clearly if it's intentional.

🔎 Proposed fix for single-thumb default
  const _values = React.useMemo(
    () =>
      Array.isArray(value)
        ? value
        : Array.isArray(defaultValue)
          ? defaultValue
-          : [min, max],
+          : [min],
    [value, defaultValue, min, max],
  )
kits/automation/blog-automation/components/ui/textarea.tsx-10-10 (1)

10-10: Add feature detection or fallback for field-sizing-content.

The field-sizing-content CSS property is currently supported only in Chromium-based browsers (Chrome 123+, Edge 123+, Chrome Android 123+). Firefox and Safari do not yet support it. Use CSS feature detection (@supports field-sizing: content or CSS.supports('field-sizing: content')) to provide fallback sizing behavior for unsupported browsers, or specify min/max height constraints as a baseline.

kits/automation/blog-automation/orchestrate.js-21-37 (1)

21-37: Fix polling value type in SEO flow.

Same issue as the drafting flow - polling should be boolean false instead of string "false".

🔎 Proposed fix
       "mode": "sync",
-      "polling": "false"
+      "polling": false
   },
kits/automation/blog-automation/orchestrate.js-1-20 (1)

1-20: Fix polling value type.

The polling property is set to string "false" instead of boolean false. This could cause issues if the orchestration engine expects a boolean type.

🔎 Proposed fix for drafting flow
       "mode": "sync",
-      "polling": "false"
+      "polling": false
   },
kits/automation/blog-automation/orchestrate.js-38-55 (1)

38-55: Fix polling value type in publish flow.

Same issue - polling should be boolean false instead of string "false".

🔎 Proposed fix
       "mode": "sync",
-      "polling": "false"
+      "polling": false
   }
kits/automation/blog-automation/components/ui/kbd.tsx-18-26 (1)

18-26: Fix type mismatch in KbdGroup.

KbdGroup accepts React.ComponentProps<'div'> but renders a <kbd> element. This creates a type safety issue where div-specific attributes could be passed but won't be valid on a kbd element.

🔎 Proposed fix
-function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {
+function KbdGroup({ className, ...props }: React.ComponentProps<'kbd'>) {
   return (
     <kbd
kits/automation/blog-automation/app/page.tsx-51-68 (1)

51-68: Step state never progresses beyond "drafting" during execution.

The step state is set to "drafting" at the start but is never updated to "seo" or "publishing" during the automation. The loading text (Line 182) that shows different messages based on step will always display "AI Drafting..." regardless of actual progress.

If step-based progress feedback is desired, you'll need to either:

  1. Update the step state during execution (requires callback/event mechanism from runBlogAutomation)
  2. Remove the step-based messaging if granular progress isn't available
kits/automation/blog-automation/hooks/use-toast.ts-174-182 (1)

174-182: Incorrect dependency array causes re-subscription on every state change.

The useEffect depends on state, but it only needs to subscribe once on mount. This causes the listener to be removed and re-added on every state update, which is inefficient and could cause missed updates.

🔎 Proposed fix
   React.useEffect(() => {
     listeners.push(setState)
     return () => {
       const index = listeners.indexOf(setState)
       if (index > -1) {
         listeners.splice(index, 1)
       }
     }
-  }, [state])
+  }, [])
kits/automation/blog-automation/components/ui/use-toast.ts-174-182 (1)

174-182: Incorrect dependency array causes re-subscription on every state change.

Same issue as in hooks/use-toast.ts - the useEffect should use an empty dependency array to subscribe once on mount.

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

71-81: Fix type/implementation mismatch in EmptyDescription.

The component accepts React.ComponentProps<'p'> but renders a <div>. This creates a type mismatch.

🔎 Proposed fix
-function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
+function EmptyDescription({ className, ...props }: React.ComponentProps<'div'>) {
   return (
     <div

Alternatively, if <p> semantics are intended:

 function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
   return (
-    <div
+    <p
       data-slot="empty-description"
kits/automation/blog-automation/components/ui/form.tsx-45-66 (1)

45-66: Context validation check is ineffective.

The check if (!fieldContext) on line 52 will never evaluate to true because useContext(FormFieldContext) returns the default value {} (an empty object, which is truthy) when not inside a FormField. This means the error will never be thrown, and subsequent code accessing fieldContext.name will silently return undefined.

🔎 Proposed fix
 const useFormField = () => {
   const fieldContext = React.useContext(FormFieldContext)
   const itemContext = React.useContext(FormItemContext)
   const { getFieldState } = useFormContext()
   const formState = useFormState({ name: fieldContext.name })
   const fieldState = getFieldState(fieldContext.name, formState)

-  if (!fieldContext) {
+  if (!fieldContext.name) {
     throw new Error('useFormField should be used within <FormField>')
   }

   const { id } = itemContext
🧹 Nitpick comments (33)
kits/automation/blog-automation/components/ui/alert-dialog.tsx (1)

121-143: Consider adding data-slot attributes for consistency.

AlertDialogAction and AlertDialogCancel are missing the data-slot attribute that all other components in this file consistently include. Adding these attributes would maintain the pattern established throughout the component and could be useful for CSS selectors, testing utilities, or debugging tools.

🔎 Suggested additions for consistency
 function AlertDialogAction({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
   return (
     <AlertDialogPrimitive.Action
+      data-slot="alert-dialog-action"
       className={cn(buttonVariants(), className)}
       {...props}
     />
   )
 }

 function AlertDialogCancel({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
   return (
     <AlertDialogPrimitive.Cancel
+      data-slot="alert-dialog-cancel"
       className={cn(buttonVariants({ variant: 'outline' }), className)}
       {...props}
     />
   )
 }
kits/automation/blog-automation/components/ui/scroll-area.tsx (2)

8-29: Well-structured ScrollArea component.

The implementation correctly wraps Radix UI primitives with proper prop spreading and type safety. The focus-visible styles provide custom focus indicators while maintaining accessibility.

Consider forwarding refs for enhanced composability, though this is optional and may depend on your library's design patterns.

🔎 Optional: Forward refs for better composability
-function ScrollArea({
+const ScrollArea = React.forwardRef<
+  React.ElementRef<typeof ScrollAreaPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
+>(({
   className,
   children,
   ...props
-}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
+}, ref) => {
   return (
     <ScrollAreaPrimitive.Root
+      ref={ref}
       data-slot="scroll-area"
       className={cn('relative', className)}
       {...props}
     >
       <ScrollAreaPrimitive.Viewport
         data-slot="scroll-area-viewport"
         className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
       >
         {children}
       </ScrollAreaPrimitive.Viewport>
       <ScrollBar />
       <ScrollAreaPrimitive.Corner />
     </ScrollAreaPrimitive.Root>
   )
-}
+})
+ScrollArea.displayName = 'ScrollArea'

31-58: Clean ScrollBar implementation with proper orientation handling.

The component correctly implements conditional styling for vertical and horizontal orientations, with sensible defaults. The touch-none and select-none classes provide appropriate scrollbar behavior. Exports are properly defined.

Similar to ScrollArea, consider forwarding refs for enhanced composability (optional).

🔎 Optional: Forward refs for ScrollBar
-function ScrollBar({
+const ScrollBar = React.forwardRef<
+  React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
+  React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
+>(({
   className,
   orientation = 'vertical',
   ...props
-}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
+}, ref) => {
   return (
     <ScrollAreaPrimitive.ScrollAreaScrollbar
+      ref={ref}
       data-slot="scroll-area-scrollbar"
       orientation={orientation}
       className={cn(
         'flex touch-none p-px transition-colors select-none',
         orientation === 'vertical' &&
           'h-full w-2.5 border-l border-l-transparent',
         orientation === 'horizontal' &&
           'h-2.5 flex-col border-t border-t-transparent',
         className,
       )}
       {...props}
     >
       <ScrollAreaPrimitive.ScrollAreaThumb
         data-slot="scroll-area-thumb"
         className="bg-border relative flex-1 rounded-full"
       />
     </ScrollAreaPrimitive.ScrollAreaScrollbar>
   )
-}
+})
+ScrollBar.displayName = 'ScrollBar'
kits/automation/blog-automation/components/ui/radio-group.tsx (1)

35-40: Consider simplifying the centering approach.

The Indicator uses flexbox centering (flex items-center justify-center) while the CircleIcon uses absolute positioning with transforms. Since absolute positioning removes an element from the flex layout, only the absolute positioning is effective here, making the flex properties redundant.

🔎 Suggested simplification

Option A: Use flexbox centering only (cleaner)

       <RadioGroupPrimitive.Indicator
         data-slot="radio-group-indicator"
         className="relative flex items-center justify-center"
       >
-        <CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
+        <CircleIcon className="fill-primary size-2" />
       </RadioGroupPrimitive.Indicator>

Option B: Use absolute positioning only

       <RadioGroupPrimitive.Indicator
         data-slot="radio-group-indicator"
-        className="relative flex items-center justify-center"
+        className="relative"
       >
         <CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
       </RadioGroupPrimitive.Indicator>
kits/automation/blog-automation/components/ui/resizable.tsx (1)

31-54: Well-implemented handle with good flexibility.

The component correctly handles both horizontal and vertical orientations, and the withHandle prop provides useful control over the visual indicator.

Consider adding an aria-label to the inner handle div for improved accessibility:

🔎 Optional accessibility enhancement
      {withHandle && (
-       <div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
+       <div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border" aria-label="Resize handle">
          <GripVerticalIcon className="size-2.5" />
        </div>
      )}
kits/automation/blog-automation/components/ui/item.tsx (2)

33-72: Consider minor cleanups (optional).

Two small refinements you could make:

  1. Line 34: The transition-colors class appears both unconditionally and within the [a&]: selector, which seems redundant.
  2. Line 43: Trailing space after 'gap-4 '.

The Item component implementation itself is excellent—good use of the Radix asChild pattern and proper TypeScript typing.

🔎 Proposed cleanup
-  'group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a&]:hover:bg-accent/50 [a&]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
+  'group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a&]:hover:bg-accent/50 duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
   {
     variants: {
       variant: {
         default: 'bg-transparent',
         outline: 'border-border',
         muted: 'bg-muted/50',
       },
       size: {
-        default: 'p-4 gap-4 ',
+        default: 'p-4 gap-4',
         sm: 'py-3 px-4 gap-2.5',
       },
     },

119-130: Consider semantic heading support for ItemTitle (optional).

ItemTitle currently uses a <div>, which provides flexibility but lacks semantic meaning. For better accessibility and SEO, you might consider making it polymorphic to support heading elements (h1-h6) when appropriate.

💡 Example polymorphic approach
function ItemTitle({ 
  className, 
  as: Component = 'div',
  ...props 
}: React.ComponentProps<'div'> & { as?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' }) {
  return (
    <Component
      data-slot="item-title"
      className={cn(
        'flex w-fit items-center gap-2 text-sm leading-snug font-medium',
        className,
      )}
      {...props}
    />
  )
}

This allows consumers to specify <ItemTitle as="h2">...</ItemTitle> when needed.

kits/automation/blog-automation/components/ui/chart.tsx (2)

182-189: Potential duplicate React key when dataKey is undefined.

If item.dataKey is undefined for multiple items, React will warn about duplicate keys and may have issues with list reconciliation.

🔎 Suggested fix using index as fallback
-              key={item.dataKey}
+              key={item.dataKey ?? `item-${index}`}

278-301: Potential duplicate React key when legend items share the same value.

Using item.value as the key could cause issues if multiple legend items have identical values (e.g., same label text).

🔎 Suggested fix using dataKey or index
-          <div
-            key={item.value}
+          <div
+            key={item.dataKey || item.value}
kits/automation/blog-automation/components/ui/toggle.tsx (1)

31-45: Consider adding forwardRef for improved ref forwarding.

The component is well-implemented but lacks ref forwarding, which is a common pattern in reusable UI libraries. Consumers may need ref access for focus management, animations, or measuring.

🔎 Suggested enhancement with forwardRef
-function Toggle({
+const Toggle = React.forwardRef<
+  React.ElementRef<typeof TogglePrimitive.Root>,
+  React.ComponentProps<typeof TogglePrimitive.Root> &
+    VariantProps<typeof toggleVariants>
+>(({
   className,
   variant,
   size,
   ...props
-}: React.ComponentProps<typeof TogglePrimitive.Root> &
-  VariantProps<typeof toggleVariants>) {
+}, ref) => {
   return (
     <TogglePrimitive.Root
       data-slot="toggle"
+      ref={ref}
       className={cn(toggleVariants({ variant, size, className }))}
       {...props}
     />
   )
-}
+})
+Toggle.displayName = 'Toggle'
kits/automation/blog-automation/components/ui/toggle-group.tsx (2)

17-41: Consider adding forwardRef to ToggleGroup.

The component correctly implements the context provider pattern and styling. However, adding ref forwarding would improve flexibility for consumers who need DOM access.

🔎 Suggested enhancement with forwardRef
-function ToggleGroup({
+const ToggleGroup = React.forwardRef<
+  React.ElementRef<typeof ToggleGroupPrimitive.Root>,
+  React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
+    VariantProps<typeof toggleVariants>
+>(({
   className,
   variant,
   size,
   children,
   ...props
-}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
-  VariantProps<typeof toggleVariants>) {
+}, ref) => {
   return (
     <ToggleGroupPrimitive.Root
       data-slot="toggle-group"
       data-variant={variant}
       data-size={size}
+      ref={ref}
       className={cn(
         'group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs',
         className,
       )}
       {...props}
     >
       <ToggleGroupContext.Provider value={{ variant, size }}>
         {children}
       </ToggleGroupContext.Provider>
     </ToggleGroupPrimitive.Root>
   )
-}
+})
+ToggleGroup.displayName = 'ToggleGroup'

43-71: Consider adding forwardRef to ToggleGroupItem.

The component correctly consumes context for variant/size fallback and applies appropriate styling for grouped items. The focus z-index management and border handling are well-implemented. Adding ref forwarding would complete the component API.

🔎 Suggested enhancement with forwardRef
-function ToggleGroupItem({
+const ToggleGroupItem = React.forwardRef<
+  React.ElementRef<typeof ToggleGroupPrimitive.Item>,
+  React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
+    VariantProps<typeof toggleVariants>
+>(({
   className,
   children,
   variant,
   size,
   ...props
-}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
-  VariantProps<typeof toggleVariants>) {
+}, ref) => {
   const context = React.useContext(ToggleGroupContext)
 
   return (
     <ToggleGroupPrimitive.Item
       data-slot="toggle-group-item"
       data-variant={context.variant || variant}
       data-size={context.size || size}
+      ref={ref}
       className={cn(
         toggleVariants({
           variant: context.variant || variant,
           size: context.size || size,
         }),
         'min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l',
         className,
       )}
       {...props}
     >
       {children}
     </ToggleGroupPrimitive.Item>
   )
-}
+})
+ToggleGroupItem.displayName = 'ToggleGroupItem'
kits/automation/blog-automation/components/ui/collapsible.tsx (1)

5-31: Consider adding explicit return types.

While TypeScript correctly infers the return types, adding explicit React.ReactElement or JSX.Element return types would improve code maintainability and self-documentation.

🔎 Proposed refactor with explicit return types
 function Collapsible({
   ...props
-}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
+}: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.ReactElement {
   return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
 }
 
 function CollapsibleTrigger({
   ...props
-}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>): React.ReactElement {
   return (
     <CollapsiblePrimitive.CollapsibleTrigger
       data-slot="collapsible-trigger"
       {...props}
     />
   )
 }
 
 function CollapsibleContent({
   ...props
-}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>): React.ReactElement {
   return (
     <CollapsiblePrimitive.CollapsibleContent
       data-slot="collapsible-content"
       {...props}
     />
   )
 }
kits/automation/blog-automation/components/ui/drawer.tsx (2)

48-73: Redundant data-slot prop on DrawerPortal.

Line 54 passes data-slot="drawer-portal" to <DrawerPortal>, but DrawerPortal already sets this attribute internally (line 23). The prop is harmless but unnecessary.

🔎 Proposed cleanup
-    <DrawerPortal data-slot="drawer-portal">
+    <DrawerPortal>

1-73: Test keyboard and focus behavior thoroughly—vaul has known edge cases.

Vaul 0.9.9 is React-compatible and provides ARIA semantics, keyboard navigation (Escape, Tab), and focus management. However, test for reported issues: aria-hidden console errors when opening via keyboard, and focus becoming trapped when using modal={false} with snapPoints or always-open configurations. Include keyboard and screen reader testing (VoiceOver, NVDA, JAWS) in your validation before deployment.

kits/automation/blog-automation/README.md (1)

15-15: Minor redundancy: "SEO optimization" is technically redundant.

The phrase "SEO optimization" is redundant since SEO already stands for "Search Engine Optimization." Consider using simply "SEO" or "search engine optimization."

🔎 Proposed fix
-3.  **AI Drafting & SEO**: The agent drafts a blog post using AI, ensuring deep SEO optimization, coherence, and technical accuracy.
+3.  **AI Drafting & SEO**: The agent drafts a blog post using AI, ensuring deep SEO, coherence, and technical accuracy.
kits/automation/blog-automation/flows/blog-seo/README.md (2)

1-1: Consider simplifying the redundant title.

The title "SEO Optimization Flow" is redundant since SEO already stands for "Search Engine Optimization." Consider simplifying to "SEO Flow" or "Search Engine Optimization Flow."

🔎 Proposed fix
-# SEO Optimization Flow
+# SEO Flow

7-9: Specify language for the code block.

The fenced code block should specify a language identifier for proper syntax highlighting and rendering.

🔎 Proposed fix
-```
+```mermaid
 [API Request] → [SEO Optimize Content (LLM)] → [API Response]

Or if it's meant to be plain text:

```diff
-```
+```text
 [API Request] → [SEO Optimize Content (LLM)] → [API Response]
</details>

</blockquote></details>
<details>
<summary>kits/automation/blog-automation/flows/blog-drafting/README.md (1)</summary><blockquote>

`7-9`: **Add language identifier to the fenced code block.**

The fenced code block should specify a language identifier for proper rendering and markdown compliance.



<details>
<summary>🔎 Proposed fix</summary>

```diff
-```
+```text
 [API Request] → [Generate Blog Draft (LLM)] → [API Response]
</details>

</blockquote></details>
<details>
<summary>kits/automation/blog-automation/flows/blog-drafting/meta.json (1)</summary><blockquote>

`15-17`: **Consider populating or removing empty URL fields.**

The `githubUrl`, `documentationUrl`, and `deployUrl` fields are set to empty strings. If these won't be populated, consider removing them or setting them to `null` for clearer intent.

</blockquote></details>
<details>
<summary>kits/automation/blog-automation/flows/blog-seo/meta.json (1)</summary><blockquote>

`14-16`: **Consider populating or removing empty URL fields.**

The `githubUrl`, `documentationUrl`, and `deployUrl` fields are set to empty strings. If these won't be populated, consider removing them or setting them to `null` for clearer intent.

</blockquote></details>
<details>
<summary>kits/automation/blog-automation/flows/blog-publish/README.md (1)</summary><blockquote>

`7-9`: **Add language specifier to fenced code block.**

The flow diagram uses a fenced code block without a language identifier, which triggers linter warnings.



<details>
<summary>🔎 Proposed fix</summary>

```diff
-```
+```text
 [API Request] → [Publish to WordPress (API)] → [API Response]
</details>

</blockquote></details>
<details>
<summary>kits/automation/blog-automation/app/layout.tsx (1)</summary><blockquote>

`2-7`: **Unused font imports—apply or remove.**

The `Geist` and `Geist_Mono` fonts are imported but never applied. The variables are prefixed with `_` (indicating intentional non-use), yet the body uses `font-sans` which doesn't reference them.

If the fonts are meant to be used, apply them to the body element. Otherwise, remove the imports to reduce bundle size.



<details>
<summary>🔎 Option 1: Apply the fonts</summary>

```diff
-const _geist = Geist({ subsets: ["latin"] });
-const _geistMono = Geist_Mono({ subsets: ["latin"] });
+const geist = Geist({ 
+  subsets: ["latin"],
+  variable: "--font-sans",
+});
+const geistMono = Geist_Mono({ 
+  subsets: ["latin"],
+  variable: "--font-mono",
+});
-      <body className={`font-sans antialiased`}>
+      <body className={`${geist.variable} ${geistMono.variable} font-sans antialiased`}>

Option 2: Remove the unused imports

-import { Geist, Geist_Mono } from 'next/font/google'
-
-const _geist = Geist({ subsets: ["latin"] });
-const _geistMono = Geist_Mono({ subsets: ["latin"] });
-
kits/automation/blog-automation/orchestrate.js (1)

57-61: Consider adding environment variable validation.

The configuration relies on several environment variables (AUTOMATION_BLOG_DRAFTING, AUTOMATION_BLOG_SEO, AUTOMATION_BLOG_PUBLISH, LAMATIC_API_URL, LAMATIC_PROJECT_ID, LAMATIC_API_KEY). Consider adding validation to fail fast with helpful error messages if any required variables are missing.

🔎 Example validation approach

Add validation before the config export:

const requiredEnvVars = [
  'AUTOMATION_BLOG_DRAFTING',
  'AUTOMATION_BLOG_SEO', 
  'AUTOMATION_BLOG_PUBLISH',
  'LAMATIC_API_URL',
  'LAMATIC_PROJECT_ID',
  'LAMATIC_API_KEY'
];

const missingVars = requiredEnvVars.filter(v => !process.env[v]);
if (missingVars.length > 0) {
  throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
}

export const config = {
  // ...
}
kits/automation/blog-automation/components/ui/tooltip.tsx (1)

21-29: Consider using a single TooltipProvider at app level.

Each Tooltip instance creates its own TooltipProvider wrapper. If multiple tooltips are rendered on a page, this creates unnecessary provider instances. Radix UI recommends wrapping the entire app in a single TooltipProvider for better performance.

🔎 Recommended approach

Option 1 (Recommended): Remove the provider from individual Tooltip instances and add it once at the app level:

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

Then in your root layout or app component:

<TooltipProvider>
  {/* your app */}
</TooltipProvider>

Option 2: Keep current pattern but document that it's intended for standalone use, and provide a separate composition-friendly export.

kits/automation/blog-automation/app/page.tsx (1)

319-323: Timestamp reflects render time, not actual execution time.

new Date().toLocaleTimeString() is evaluated on each render, so the "Last Execution" time will update if the component re-renders. Consider storing the execution timestamp in state when the automation completes.

🔎 Proposed approach
+  const [executedAt, setExecutedAt] = useState<Date | null>(null)
   
   // In handleSubmit, after success:
   if (response.success) {
     setResult(response)
+    setExecutedAt(new Date())
   }
   
   // In the timestamp display:
-  <p className="text-slate-500 dark:text-slate-400 font-medium">Today at {new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</p>
+  <p className="text-slate-500 dark:text-slate-400 font-medium">Today at {executedAt?.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</p>
kits/automation/blog-automation/hooks/use-toast.ts (1)

1-191: Remove duplicate file and consolidate use-toast implementation.

This file is identical to kits/automation/blog-automation/components/ui/use-toast.ts. Having duplicate implementations creates maintenance burden and risk of divergence. Keep only one file and re-export from the other location, or remove the duplicate entirely.

kits/automation/blog-automation/actions/orchestrate.ts (3)

23-23: Consider using a server-side environment variable for mock mode.

The NEXT_PUBLIC_MOCK_MODE prefix exposes this variable to the browser bundle. Since this is a server action ("use server"), prefer a non-prefixed environment variable like MOCK_MODE to avoid unnecessary client exposure.

🔎 Proposed fix
-    if (process.env.NEXT_PUBLIC_MOCK_MODE === "true" || !process.env.LAMATIC_API_KEY) {
+    if (process.env.MOCK_MODE === "true" || !process.env.LAMATIC_API_KEY) {

39-47: Clarify duplicate key behavior in payloads.

The payload includes both lowercase and capitalized versions of the same keys (e.g., topic and Topic). In JavaScript objects, duplicate keys result in the last value overwriting earlier ones, so only the capitalized versions will be retained. If this is intentional to support specific Lamatic flow input mappings, consider adding a comment explaining this behavior to prevent future confusion.


115-117: Consider stricter URL validation for successful publishes.

The current logic allows a successful status without a URL. If a URL is always expected on successful publish, consider updating the validation:

Suggested stricter validation
-    if (status !== "success" && !url) {
+    if (status !== "success" || !url) {
       throw new Error(`Publishing failed: Response status was '${rawStatus}' and no URL was found.`)
     }

Only apply this if your CMS workflows always return a URL on success.

kits/automation/blog-automation/components/ui/pagination.tsx (1)

9-9: Unused import: Button is imported but never used.

Only buttonVariants is utilized in this file. Remove the unused import to keep the code clean.

🔎 Proposed fix
-import { Button, buttonVariants } from '@/components/ui/button'
+import { buttonVariants } from '@/components/ui/button'
kits/automation/blog-automation/components/ui/menubar.tsx (1)

219-241: Minor class inconsistency: outline-none vs outline-hidden.

MenubarSubTrigger uses outline-none (line 232), while similar components in context-menu.tsx use outline-hidden. This appears to be a minor inconsistency that doesn't affect functionality but could be aligned for consistency.

🔎 Align with other components
       className={cn(
-        'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8',
+        'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
         className,
       )}
kits/automation/blog-automation/components/ui/sidebar.tsx (1)

8-8: Consolidate duplicate useIsMobile hook across kits.

The hook exists in duplicate form in both hooks/use-mobile.ts and components/ui/use-mobile.tsx throughout the codebase. This pattern is systemic—all 8 kits (blog-automation, agentic/deep-search, agentic/generation, embed/chat, embed/search, embed/sheets, automation/hiring, sample/content-generation) contain identical implementations in both locations. Consider establishing a single source of truth at the hooks level and removing the components/ui copies to reduce maintenance overhead.

📜 Review details

Configuration used: defaults

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 4d04236 and 9481553.

⛔ Files ignored due to path filters (1)
  • kits/automation/blog-automation/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (89)
  • kits/automation/blog-automation/.env.example
  • kits/automation/blog-automation/.gitignore
  • kits/automation/blog-automation/README.md
  • kits/automation/blog-automation/actions/orchestrate.ts
  • kits/automation/blog-automation/app/globals.css
  • kits/automation/blog-automation/app/layout.tsx
  • kits/automation/blog-automation/app/page.tsx
  • kits/automation/blog-automation/components.json
  • kits/automation/blog-automation/components/header.tsx
  • kits/automation/blog-automation/components/theme-provider.tsx
  • kits/automation/blog-automation/components/ui/accordion.tsx
  • kits/automation/blog-automation/components/ui/alert-dialog.tsx
  • kits/automation/blog-automation/components/ui/alert.tsx
  • kits/automation/blog-automation/components/ui/aspect-ratio.tsx
  • kits/automation/blog-automation/components/ui/avatar.tsx
  • kits/automation/blog-automation/components/ui/badge.tsx
  • kits/automation/blog-automation/components/ui/breadcrumb.tsx
  • kits/automation/blog-automation/components/ui/button-group.tsx
  • kits/automation/blog-automation/components/ui/button.tsx
  • kits/automation/blog-automation/components/ui/calendar.tsx
  • kits/automation/blog-automation/components/ui/card.tsx
  • kits/automation/blog-automation/components/ui/carousel.tsx
  • kits/automation/blog-automation/components/ui/chart.tsx
  • kits/automation/blog-automation/components/ui/checkbox.tsx
  • kits/automation/blog-automation/components/ui/collapsible.tsx
  • kits/automation/blog-automation/components/ui/command.tsx
  • kits/automation/blog-automation/components/ui/context-menu.tsx
  • kits/automation/blog-automation/components/ui/dialog.tsx
  • kits/automation/blog-automation/components/ui/drawer.tsx
  • kits/automation/blog-automation/components/ui/dropdown-menu.tsx
  • kits/automation/blog-automation/components/ui/empty.tsx
  • kits/automation/blog-automation/components/ui/field.tsx
  • kits/automation/blog-automation/components/ui/form.tsx
  • kits/automation/blog-automation/components/ui/hover-card.tsx
  • kits/automation/blog-automation/components/ui/input-group.tsx
  • kits/automation/blog-automation/components/ui/input-otp.tsx
  • kits/automation/blog-automation/components/ui/input.tsx
  • kits/automation/blog-automation/components/ui/item.tsx
  • kits/automation/blog-automation/components/ui/kbd.tsx
  • kits/automation/blog-automation/components/ui/label.tsx
  • kits/automation/blog-automation/components/ui/menubar.tsx
  • kits/automation/blog-automation/components/ui/navigation-menu.tsx
  • kits/automation/blog-automation/components/ui/pagination.tsx
  • kits/automation/blog-automation/components/ui/popover.tsx
  • kits/automation/blog-automation/components/ui/progress.tsx
  • kits/automation/blog-automation/components/ui/radio-group.tsx
  • kits/automation/blog-automation/components/ui/resizable.tsx
  • kits/automation/blog-automation/components/ui/scroll-area.tsx
  • kits/automation/blog-automation/components/ui/select.tsx
  • kits/automation/blog-automation/components/ui/separator.tsx
  • kits/automation/blog-automation/components/ui/sheet.tsx
  • kits/automation/blog-automation/components/ui/sidebar.tsx
  • kits/automation/blog-automation/components/ui/skeleton.tsx
  • kits/automation/blog-automation/components/ui/slider.tsx
  • kits/automation/blog-automation/components/ui/sonner.tsx
  • kits/automation/blog-automation/components/ui/spinner.tsx
  • kits/automation/blog-automation/components/ui/switch.tsx
  • kits/automation/blog-automation/components/ui/table.tsx
  • kits/automation/blog-automation/components/ui/tabs.tsx
  • kits/automation/blog-automation/components/ui/textarea.tsx
  • kits/automation/blog-automation/components/ui/toast.tsx
  • kits/automation/blog-automation/components/ui/toaster.tsx
  • kits/automation/blog-automation/components/ui/toggle-group.tsx
  • kits/automation/blog-automation/components/ui/toggle.tsx
  • kits/automation/blog-automation/components/ui/tooltip.tsx
  • kits/automation/blog-automation/components/ui/use-mobile.tsx
  • kits/automation/blog-automation/components/ui/use-toast.ts
  • kits/automation/blog-automation/config.json
  • kits/automation/blog-automation/flows/blog-drafting/README.md
  • kits/automation/blog-automation/flows/blog-drafting/config.json
  • kits/automation/blog-automation/flows/blog-drafting/inputs.json
  • kits/automation/blog-automation/flows/blog-drafting/meta.json
  • kits/automation/blog-automation/flows/blog-publish/README.md
  • kits/automation/blog-automation/flows/blog-publish/config.json
  • kits/automation/blog-automation/flows/blog-publish/inputs.json
  • kits/automation/blog-automation/flows/blog-publish/meta.json
  • kits/automation/blog-automation/flows/blog-seo/README.md
  • kits/automation/blog-automation/flows/blog-seo/config.json
  • kits/automation/blog-automation/flows/blog-seo/inputs.json
  • kits/automation/blog-automation/flows/blog-seo/meta.json
  • kits/automation/blog-automation/hooks/use-mobile.ts
  • kits/automation/blog-automation/hooks/use-toast.ts
  • kits/automation/blog-automation/lib/lamatic-client.ts
  • kits/automation/blog-automation/lib/utils.ts
  • kits/automation/blog-automation/next.config.mjs
  • kits/automation/blog-automation/orchestrate.js
  • kits/automation/blog-automation/package.json
  • kits/automation/blog-automation/postcss.config.mjs
  • kits/automation/blog-automation/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (56)
kits/automation/blog-automation/components/ui/alert.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/progress.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/switch.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/use-mobile.tsx (1)
kits/automation/blog-automation/hooks/use-mobile.ts (1)
  • useIsMobile (5-19)
kits/automation/blog-automation/components/ui/separator.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/toggle.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/checkbox.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/accordion.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/button.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/toggle-group.tsx (2)
kits/automation/blog-automation/components/ui/toggle.tsx (1)
  • toggleVariants (47-47)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/spinner.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/textarea.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/scroll-area.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/postcss.config.mjs (1)
kits/automation/blog-automation/orchestrate.js (2)
  • config (1-62)
  • config (1-62)
kits/automation/blog-automation/components/ui/badge.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/actions/orchestrate.ts (3)
kits/automation/blog-automation/orchestrate.js (2)
  • config (1-62)
  • config (1-62)
kits/automation/blog-automation/postcss.config.mjs (1)
  • config (2-6)
kits/automation/blog-automation/lib/lamatic-client.ts (1)
  • lamaticClient (18-22)
kits/automation/blog-automation/components/ui/calendar.tsx (2)
kits/automation/blog-automation/components/ui/button.tsx (2)
  • Button (60-60)
  • buttonVariants (60-60)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/tooltip.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/hooks/use-mobile.ts (1)
kits/automation/blog-automation/components/ui/use-mobile.tsx (1)
  • useIsMobile (5-19)
kits/automation/blog-automation/components/ui/input.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/kbd.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/hover-card.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/navigation-menu.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/lib/lamatic-client.ts (1)
kits/automation/blog-automation/orchestrate.js (2)
  • config (1-62)
  • config (1-62)
kits/automation/blog-automation/components/ui/breadcrumb.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/table.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/slider.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/app/page.tsx (2)
kits/automation/blog-automation/actions/orchestrate.ts (2)
  • BlogAutomationResult (6-12)
  • runBlogAutomation (14-133)
kits/automation/blog-automation/components/header.tsx (1)
  • Header (4-41)
kits/automation/blog-automation/components/ui/pagination.tsx (2)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/button.tsx (2)
  • Button (60-60)
  • buttonVariants (60-60)
kits/automation/blog-automation/components/ui/input-otp.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/avatar.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/button-group.tsx (2)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/separator.tsx (1)
  • Separator (28-28)
kits/automation/blog-automation/components/ui/sonner.tsx (1)
kits/automation/blog-automation/components/ui/toaster.tsx (1)
  • Toaster (13-35)
kits/automation/blog-automation/components/ui/command.tsx (2)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/dialog.tsx (5)
  • Dialog (133-133)
  • DialogHeader (138-138)
  • DialogTitle (141-141)
  • DialogDescription (136-136)
  • DialogContent (135-135)
kits/automation/blog-automation/components/ui/drawer.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/popover.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/label.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/radio-group.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/skeleton.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/empty.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/carousel.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/item.tsx (2)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/separator.tsx (1)
  • Separator (28-28)
kits/automation/blog-automation/components/ui/dialog.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/form.tsx (2)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/label.tsx (1)
  • Label (24-24)
kits/automation/blog-automation/components/ui/toast.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/select.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/tabs.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/context-menu.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/input-group.tsx (4)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/button.tsx (1)
  • Button (60-60)
kits/automation/blog-automation/components/ui/input.tsx (1)
  • Input (21-21)
kits/automation/blog-automation/components/ui/textarea.tsx (1)
  • Textarea (18-18)
kits/automation/blog-automation/components/ui/resizable.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/toaster.tsx (4)
kits/automation/blog-automation/components/ui/sonner.tsx (1)
  • Toaster (25-25)
kits/automation/blog-automation/components/ui/use-toast.ts (1)
  • useToast (191-191)
kits/automation/blog-automation/hooks/use-toast.ts (1)
  • useToast (191-191)
kits/automation/blog-automation/components/ui/toast.tsx (6)
  • ToastProvider (122-122)
  • Toast (124-124)
  • ToastTitle (125-125)
  • ToastDescription (126-126)
  • ToastClose (127-127)
  • ToastViewport (123-123)
kits/automation/blog-automation/components/ui/use-toast.ts (1)
kits/automation/blog-automation/hooks/use-toast.ts (2)
  • reducer (74-127)
  • toast (191-191)
kits/automation/blog-automation/components/ui/sidebar.tsx (8)
kits/automation/blog-automation/hooks/use-mobile.ts (1)
  • useIsMobile (5-19)
kits/automation/blog-automation/components/ui/tooltip.tsx (4)
  • TooltipProvider (61-61)
  • TooltipContent (61-61)
  • Tooltip (61-61)
  • TooltipTrigger (61-61)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/sheet.tsx (5)
  • Sheet (131-131)
  • SheetContent (134-134)
  • SheetHeader (135-135)
  • SheetTitle (137-137)
  • SheetDescription (138-138)
kits/automation/blog-automation/components/ui/button.tsx (1)
  • Button (60-60)
kits/automation/blog-automation/components/ui/input.tsx (1)
  • Input (21-21)
kits/automation/blog-automation/components/ui/separator.tsx (1)
  • Separator (28-28)
kits/automation/blog-automation/components/ui/skeleton.tsx (1)
  • Skeleton (13-13)
kits/automation/blog-automation/components/ui/menubar.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/sheet.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
kits/automation/blog-automation/components/ui/dropdown-menu.tsx (1)
kits/automation/blog-automation/lib/utils.ts (1)
  • cn (4-6)
🪛 ast-grep (0.40.3)
kits/automation/blog-automation/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)
kits/automation/blog-automation/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)

🪛 dotenv-linter (4.0.0)
kits/automation/blog-automation/.env.example

[warning] 2-2: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 3-3: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 4-4: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 4-4: [UnorderedKey] The AUTOMATION_BLOG_PUBLISH key should go before the AUTOMATION_BLOG_SEO key

(UnorderedKey)


[warning] 7-7: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 8-8: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 9-9: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 9-9: [UnorderedKey] The LAMATIC_API_KEY key should go before the LAMATIC_API_URL key

(UnorderedKey)

🪛 LanguageTool
kits/automation/blog-automation/flows/blog-seo/README.md

[style] ~1-~1: This phrase is redundant (‘O’ stands for ‘Optimization’). Use simply “SEO”.
Context: # SEO Optimization Flow This flow optimizes a blog post d...

(ACRONYM_TAUTOLOGY)


[style] ~16-~16: This phrase is redundant (‘O’ stands for ‘optimization’). Use simply “SEO”.
Context: ...s` | string | Yes | Target keywords for SEO optimization | ## Output Schema | Field | Type | D...

(ACRONYM_TAUTOLOGY)

kits/automation/blog-automation/README.md

[style] ~15-~15: This phrase is redundant (‘O’ stands for ‘optimization’). Use simply “SEO”.
Context: ...fts a blog post using AI, ensuring deep SEO optimization, coherence, and technical accuracy. 4. ...

(ACRONYM_TAUTOLOGY)

🪛 markdownlint-cli2 (0.18.1)
kits/automation/blog-automation/flows/blog-publish/README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

kits/automation/blog-automation/flows/blog-seo/README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

kits/automation/blog-automation/flows/blog-drafting/README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Copy link

@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: 2

♻️ Duplicate comments (3)
kits/automation/blog-automation/flows/blog-publish/config.json (1)

33-42: LGTM: Environment variable interpolation correctly implemented.

The WordPress credentials now use proper template syntax ({{env.WORDPRESS_SITE_ID}} and {{env.WORDPRESS_TOKEN}}), addressing the previous review concern about hardcoded placeholders. This aligns with the environment variables defined in .env.example.

kits/automation/blog-automation/.env.example (1)

2-13: LGTM: Environment variable syntax corrected.

The spaces around equal signs have been removed, resolving the previous review concern. The format now follows the standard KEY=value pattern that .env parsers expect.

Static analysis tools flag quote characters and key ordering, but these are minor style preferences that don't impact functionality.

kits/automation/blog-automation/lib/lamatic-client.ts (1)

1-14: LGTM: All previous review concerns addressed.

The implementation now:

  1. Imports from the TypeScript orchestrate.ts file (providing proper type safety)
  2. Validates credentials upfront and throws a descriptive error if missing
  3. Creates the Lamatic client only after validation succeeds

This addresses all three major concerns from previous reviews.

🧹 Nitpick comments (4)
kits/automation/blog-automation/next.config.mjs (1)

10-12: Verify if disabling image optimization is intentional.

Setting unoptimized: true disables Next.js's automatic image optimization. For a blog automation tool that likely handles images in content, this could impact page load performance and user experience with larger image files.

If this is intentional (e.g., your deployment platform handles optimization, or you're using external CDN processing), that's fine. Otherwise, consider enabling optimization for better performance.

kits/automation/blog-automation/orchestrate.ts (1)

1-22: Consider using literal types for better type safety.

The mode and polling properties are typed as string, which allows any value. Consider using TypeScript literal types or enums to restrict these to valid values (e.g., mode: "sync" | "async", polling: "true" | "false" or boolean).

🔎 Suggested type refinements
 export interface FlowConfig {
     name: string;
     type: string;
     workflowId: string | undefined;
     description: string;
     expectedOutput: string[];
     inputSchema: Record<string, string>;
     outputSchema: Record<string, string>;
-    mode: string;
-    polling: string;
+    mode: "sync" | "async";
+    polling: boolean;
     dependsOn?: string[];
 }

Then update the config values accordingly:

-            "mode": "sync",
-            "polling": "false"
+            "mode": "sync",
+            "polling": false
kits/automation/blog-automation/actions/orchestrate.ts (2)

39-47: Consider standardizing flow input schemas to eliminate duplicate keys.

The payloads include duplicate keys with different casings (e.g., topic and Topic, keywords and Keywords) to work around inconsistent flow schemas. While this approach is pragmatic, it:

  • Makes the payload structure unclear
  • Relies on JavaScript object key overwriting behavior (the last key wins)
  • Suggests the flow schemas themselves are inconsistent
💡 Recommended approach

Standardize the flow schemas in Lamatic Studio to use a consistent casing convention (e.g., all lowercase or camelCase), then remove the duplicates:

     const payload = {
       topic,
       keywords,
       instructions,
-      // Common variations to ensure mapping
-      Topic: topic,
-      Keywords: keywords,
-      Instructions: instructions
     }

This will make the code more maintainable and eliminate ambiguity about which keys the flows actually use.

Also applies to: 64-71, 88-95


57-58: Extensive output property fallbacks suggest schema inconsistency.

The code checks multiple property names for each flow's output (e.g., generatedResponse || content || draft, url || post_url || link). This defensive approach handles variations but indicates the flow output schemas may be inconsistent or undocumented.

💡 Suggestion

Document the actual output schema for each flow in the outputSchema configuration (in orchestrate.ts), then access properties directly using the documented schema. If flows genuinely return different schemas, add a comment explaining why these fallbacks are necessary.

Alternatively, if you control the Lamatic flows, standardize their output property names to match the documented outputSchema.

Also applies to: 80-81, 105-108

📜 Review details

Configuration used: defaults

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 9481553 and 1516d11.

⛔ Files ignored due to path filters (1)
  • kits/automation/blog-automation/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • kits/automation/blog-automation/.env.example
  • kits/automation/blog-automation/actions/orchestrate.ts
  • kits/automation/blog-automation/components/ui/breadcrumb.tsx
  • kits/automation/blog-automation/components/ui/command.tsx
  • kits/automation/blog-automation/components/ui/skeleton.tsx
  • kits/automation/blog-automation/flows/blog-publish/config.json
  • kits/automation/blog-automation/lib/lamatic-client.ts
  • kits/automation/blog-automation/next.config.mjs
  • kits/automation/blog-automation/orchestrate.ts
  • kits/automation/blog-automation/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • kits/automation/blog-automation/components/ui/skeleton.tsx
  • kits/automation/blog-automation/package.json
  • kits/automation/blog-automation/components/ui/command.tsx
  • kits/automation/blog-automation/components/ui/breadcrumb.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
kits/automation/blog-automation/lib/lamatic-client.ts (1)
kits/automation/blog-automation/orchestrate.ts (1)
  • config (24-85)
🪛 dotenv-linter (4.0.0)
kits/automation/blog-automation/.env.example

[warning] 4-4: [UnorderedKey] The AUTOMATION_BLOG_PUBLISH key should go before the AUTOMATION_BLOG_SEO key

(UnorderedKey)


[warning] 7-7: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 8-8: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 9-9: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 9-9: [UnorderedKey] The LAMATIC_API_KEY key should go before the LAMATIC_API_URL key

(UnorderedKey)


[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 13-13: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)

🔇 Additional comments (5)
kits/automation/blog-automation/orchestrate.ts (2)

27-43: LGTM: Drafting flow configuration is well-defined.

The drafting flow correctly specifies input schema (topic, keywords, instructions) and output schema (draft), with appropriate synchronous execution mode.


44-85: LGTM: Sequential flow dependencies and API configuration are correct.

The dependency chain (drafting → SEO → publish) is properly defined, and API credentials correctly handle potentially undefined environment variables. The validation in lamatic-client.ts ensures these are set before use.

kits/automation/blog-automation/actions/orchestrate.ts (3)

6-18: LGTM: Type definition and function signature are well-structured.

The BlogAutomationResult type clearly defines success/failure paths with optional fields for each pipeline stage (draft, optimizedContent, url). The function signature is clean and focused.


110-117: LGTM: Robust error detection for publishing failures.

The HTML detection (lines 111-113) effectively catches CMS 404 errors that might slip through as successful responses, and the validation logic (lines 115-117) ensures the publish operation genuinely succeeded with a URL. This defensive programming prevents silent failures.


126-132: LGTM: Error handling is comprehensive.

The catch block properly logs errors and returns a standardized failure result with a clear error message. The type guard (error instanceof Error) safely handles both Error objects and unexpected thrown values.

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.

2 participants