Skip to main content
Every project includes a collapsible chat panel where you can refine scope, add new tickets, update existing ones, or remove tickets you don’t need — all through natural conversation with AI.

Opening the chat

The project chat appears as a slide-out panel on the right side of your project view:
  1. Click the chat icon in the project header
  2. The panel slides open at 380px width
  3. Your conversation history persists across sessions
Close the panel by clicking the X icon or clicking outside the panel.
The chat panel is context-aware — it knows your project name, description, and all current tickets.

Chat workflow

The chat interface follows the same conversational pattern as initial project creation:

First conversation (ticket generation)

When you open a new project’s chat:
  1. AI asks clarifying questions — 2-4 targeted questions appear in an interactive card
  2. You answer — Select multiple choice options or type freeform answers
  3. AI generates tickets — Based on your answers and project description
  4. Confirmation — “I’ve generated the tickets. Check the board and say if you’d like any changes.”
You can skip questions anytime by clicking “Skip to ticket generation” or typing “skip”, “proceed”, or “generate tickets now”.

After tickets exist

Once you have tickets, the chat becomes a refinement tool:
You: "Add a ticket for user testing"

AI: "I'll add a user testing ticket. What should it cover?"

You: "Test the new onboarding flow with 10 users"

AI: [Calls generateTickets to create new ticket]
     "Added a user testing ticket. Check the board."

Interactive question cards

When the AI asks clarifying questions, they appear in a structured card:
┌─────────────────────────────────────────────┐
Clarifying questions
│                                              │
Question 1 of 3
│                                              │
What's your primary success metric?        
│                                              │
│  ○ User activation rate
│  ○ Time to first action
│  ○ Completion rate
│  ○ Other
│                                              │
│  [Additional details (optional)]            │
│  ┌─────────────────────────────────────┐   │
│  │                                     │   │
│  └─────────────────────────────────────┘   │
│                                              │
│          [Continue] [Skip this question]    │
└─────────────────────────────────────────────┘
Features:
  • Progress indicator — “Question 1 of 3” shows your position
  • Multiple choice — Radio buttons for predefined options
  • Freeform text — Optional text area for additional context
  • Navigation — Continue to next question or skip
  • Skip all — Button to jump directly to ticket generation
Questions are tailored to your project type. A marketing campaign gets questions about audience and channels; an event gets questions about date and scale.

Message history

All chat messages persist to PostgreSQL:
model Message {
  id        String   @id
  projectId String
  project   Project  @relation(fields: [projectId], references: [id], onDelete: Cascade)
  role      String   // "user" or "assistant"
  content   String
  createdAt DateTime @default(now())
}
When you reopen a project, the chat loads your full conversation history so the AI has context about past decisions.

How the chat works

The project chat uses the Vercel AI SDK with streaming responses:

Chat initialization

const { messages, sendMessage, status, stop } = useChat({
  messages: initialMessages,  // Loaded from database
  transport: new DefaultChatTransport({
    api: "/api/chat",
    body: { project },  // Includes project ID, name, description
  }),
  onFinish  // Callback to refresh tickets after AI response
})
The project object provides context to the AI:
const PROJECT_CONTEXT_PREFIX = (project) =>
  `Current project: ${project.name} (id: ${project.id}). 
   Description: ${project.description ?? "No description"}.\n\n`
This ensures the AI knows which project it’s working on and can reference the original scope.

System prompt

The chat uses a specialized system prompt after tickets are generated:
const PROJECT_CHAT_SYSTEM_PROMPT = `
You are an expert project manager assistant.

The user has ALREADY SUBMITTED their project idea. Do NOT ask them to describe it again.

YOUR WORKFLOW:
1. Start by asking 2-4 clarifying questions tailored to this project. 
   Call setClarifyingQuestions tool FIRST, then send your message.

2. Wait for responses. After answers, you may ask follow-ups or call generateTickets.

3. If user says "skip", "generate tickets now", or similar, 
   call generateTickets IMMEDIATELY.

4. After calling generateTickets, tell the user clearly that tickets are generated 
   and invite them to check the board or ask for changes.

After tickets exist, the user can ask you to:
- Add more (generateTickets)
- Remove some (listTickets then removeTickets)
- Change tickets (listTickets then updateTickets)

Match ticket references by title when they say things like 
"remove the onboarding ticket" or "make ticket X higher priority".
`

Tool calls

The chat has access to the same AI tools as ticket generation:
  • setClarifyingQuestions — Displays interactive question cards
  • generateTickets — Creates new tickets
  • listTickets — Finds tickets by title for updates/removals
  • updateTickets — Modifies ticket fields
  • removeTickets — Deletes tickets by ID
When you reference a ticket by name (“the onboarding ticket”), the AI automatically calls listTickets to find the matching ID, then performs the requested operation.

Chat UI components

The interface uses custom AI components:

Conversation container

<Conversation className="min-h-full">
  <ConversationContent className="px-0">
    {/* Messages or question cards */}
  </ConversationContent>
  <ConversationScrollButton />  {/* Appears when scrolled up */}
</Conversation>

Message bubbles

<Message from="assistant">
  <MessageContent>
    <MessageResponse>
      {messageText}
    </MessageResponse>
  </MessageContent>
</Message>

<Message from="user">
  <MessageContent>
    <span className="whitespace-pre-wrap">{messageText}</span>
  </MessageContent>
</Message>
Assistant messages use <MessageResponse> to format AI output with proper markdown rendering.

Prompt input

<PromptInput onSubmit={handleSubmit}>
  <PromptInputTextarea 
    name="message" 
    placeholder="Answer the questions or ask to generate tickets…" 
  />
  <PromptInputFooter>
    <Button onClick={sendSkip}>Skip to ticket generation</Button>
    <PromptInputSubmit status={status} onStop={stop} />
  </PromptInputFooter>
</PromptInput>
The footer includes a “Skip to ticket generation” button for quick access.

Loading and error states

The chat provides clear feedback:

Thinking state

While the AI processes your message:
{isLoading && (
  <Message from="assistant">
    <MessageContent>
      <Spinner className="size-4" />
      <span>Thinking…</span>
    </MessageContent>
  </Message>
)}

Error handling

If the AI encounters an error:
{error && (
  <Message from="assistant">
    <MessageContent>
      <div className="rounded-lg border border-destructive/50 bg-destructive/10 px-3 py-2">
        <p className="font-medium">Something went wrong</p>
        <p className="text-destructive/90">{getUserFriendlyErrorMessage(error)}</p>
      </div>
    </MessageContent>
  </Message>
)}
The error handler translates technical errors into user-friendly messages:
  • Rate limit errors → “We’re at capacity. Try again in 60 seconds.”
  • API key errors → “There was a problem with the service. Try again later.”
  • Other errors → “Something went wrong. Please try again.”

Empty state

Before any messages:
<ConversationEmptyState
  title="Clarifying questions"
  description="We'll ask a few questions to refine your project, then generate tickets."
>
  <Button onClick={sendSkip}>Skip to ticket generation</Button>
</ConversationEmptyState>

Ticket refresh callback

After each AI response finishes, the chat triggers a ticket refresh:
useChat({
  onFinish: () => {
    // Refetch tickets from API to show new/updated/removed tickets
    refetchTickets()
  }
})
This ensures your Kanban board updates immediately after the AI modifies tickets.

Panel behavior

The chat panel slides in/out smoothly:
<aside className={cn(
  "fixed top-0 right-0 h-dvh flex flex-col bg-background border-l",
  "transition-[width] duration-200 ease-out",
  open ? "w-[380px]" : "w-0 border-0"
)}>
  {/* Chat content */}
</aside>
Key features:
  • Fixed positioning — Overlays the Kanban board on smaller screens
  • Smooth animation — 200ms width transition
  • Zero width when closed — Doesn’t affect layout
  • 380px width — Comfortable for reading and typing
On mobile, the chat panel takes full width when open. Close it to return to the Kanban board.

Best practices

Use descriptive phrases like “the user testing ticket” or “the onboarding design task” so the AI can match by title.
You can say “Make tickets 1, 2, and 3 all P0 priority” — the AI will batch the updates.
Request context on why the AI made certain decisions: “Why did you mark this as P0?”
Start with ticket generation, review the board, then refine. Avoid trying to get everything perfect in one conversation.

Next steps

AI ticket generation

Learn how initial tickets are created

Kanban board

Organize tickets visually

Build docs developers (and LLMs) love