Claude
Skills
Sign in
Back

ai-tasks

Included with Lifetime
$97 forever

Task extraction and management — the AI creates structured tasks from conversation, persisted to Postgres with a task list UI panel. Use this skill when the user says "add tasks", "task management", "todo list", "extract tasks", or "ai tasks".

Design

What this skill does


# AI Tasks

Experience layer that extracts structured tasks from conversation and displays them in a task list panel with checkboxes, priority badges, and status management. Tasks are persisted to Postgres via Drizzle and linked to the authenticated user.

The AI creates, updates, and lists tasks via tool calls (`createTask`, `updateTask`, `listTasks`). The task list panel appears as a tab alongside the artifact panel (if installed) or as a standalone panel.

## Prerequisites

- Next.js app with `src/` directory and App Router
- `ai-core` skill installed (provides `getModel()`)
- `ai-chat` skill installed (provides chat UI, route pipeline, message renderer)
- `ai-tools` skill installed (provides tool calling framework)
- PostgreSQL with Drizzle ORM configured
- Auth skill installed (provides `requireAuth()` and `user` table reference)

## Installation

No additional packages required. Uses `ai`, `zod`, and `drizzle-orm` already installed by prerequisite skills.

## What Gets Created

```
src/
├── db/
│   └── schema/
│       └── task.ts                    # task table schema (Drizzle)
├── lib/
│   └── ai/
│       └── tools/
│           └── task.ts                # createTask, updateTask, listTasks tool definitions
└── components/
    └── ai/
        └── task-list.tsx              # Task list panel with checkboxes and priority badges
```

## What Gets Modified

```
src/
├── app/
│   ├── api/
│   │   └── ai/
│   │       └── chat/
│   │           └── route.ts           # Register task tools + system prompt
│   └── (app)/
│       └── chat/
│           └── page.tsx               # Add task list tab alongside artifacts panel
```

## Comment Slots

- **route.ts**: `// [ai-tasks]: add createTask, updateTask, listTasks` — spreads task tools into the tools object
- **route.ts**: `// [ai-tasks]: task instructions` — appends task management instructions to system prompt
- **chat/page.tsx**: `// [ai-tasks]: add panel tabs here` — adds task list panel alongside artifacts

## Database Migration

After applying this skill, create the `task` table:

```bash
bunx drizzle-kit push
```

## Setup Steps

### Step 1: Create `src/db/schema/task.ts`

```typescript
import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core";
import { user } from "./auth";

export const task = pgTable("task", {
  id: text("id")
    .primaryKey()
    .$defaultFn(() => crypto.randomUUID()),
  userId: text("user_id")
    .notNull()
    .references(() => user.id, { onDelete: "cascade" }),
  sessionId: text("session_id"),
  title: text("title").notNull(),
  description: text("description"),
  status: text("status", {
    enum: ["todo", "in_progress", "done", "cancelled"],
  })
    .notNull()
    .default("todo"),
  priority: text("priority", {
    enum: ["low", "medium", "high", "urgent"],
  })
    .notNull()
    .default("medium"),
  sortOrder: integer("sort_order").notNull().default(0),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

export type Task = typeof task.$inferSelect;
export type NewTask = typeof task.$inferInsert;
```

### Step 2: Create `src/lib/ai/tools/task.ts`

```typescript
import { tool } from "ai";
import { z } from "zod";
import { db } from "@/db";
import { task } from "@/db/schema/task";
import { eq, and, desc } from "drizzle-orm";

type TaskResult = {
  id: string;
  title: string;
  description: string | null;
  status: string;
  priority: string;
  createdAt: Date;
  updatedAt: Date;
};

type TaskListResult = {
  tasks: TaskResult[];
  total: number;
};

export function createTaskTools(userId: string, sessionId?: string) {
  const createTask = tool({
    description:
      "Create a new task or to-do item. Use this when the user mentions something they need to do, a goal, an action item, or when they ask you to track tasks.",
    inputSchema: z.object({
      title: z
        .string()
        .describe("Short, actionable title for the task"),
      description: z
        .string()
        .optional()
        .describe(
          "Detailed description with context, acceptance criteria, or notes"
        ),
      priority: z
        .enum(["low", "medium", "high", "urgent"])
        .default("medium")
        .describe(
          "Task priority: low (nice-to-have), medium (normal), high (important), urgent (do now)"
        ),
    }),
    execute: async ({ title, description, priority }): Promise<TaskResult> => {
      const maxOrder = await db
        .select({ sortOrder: task.sortOrder })
        .from(task)
        .where(eq(task.userId, userId))
        .orderBy(desc(task.sortOrder))
        .limit(1);

      const nextOrder =
        maxOrder.length > 0 ? maxOrder[0].sortOrder + 1 : 0;

      const [created] = await db
        .insert(task)
        .values({
          userId,
          sessionId,
          title,
          description: description ?? null,
          priority,
          sortOrder: nextOrder,
        })
        .returning();

      return {
        id: created.id,
        title: created.title,
        description: created.description,
        status: created.status,
        priority: created.priority,
        createdAt: created.createdAt,
        updatedAt: created.updatedAt,
      };
    },
  });

  const updateTask = tool({
    description:
      "Update an existing task's status or details. Use this when the user marks a task as done, changes priority, or modifies task details.",
    inputSchema: z.object({
      id: z.string().describe("The task ID to update"),
      status: z
        .enum(["todo", "in_progress", "done", "cancelled"])
        .optional()
        .describe("New status for the task"),
      priority: z
        .enum(["low", "medium", "high", "urgent"])
        .optional()
        .describe("New priority for the task"),
      title: z
        .string()
        .optional()
        .describe("Updated title"),
      description: z
        .string()
        .optional()
        .describe("Updated description"),
    }),
    execute: async ({
      id,
      status,
      priority,
      title,
      description,
    }): Promise<TaskResult> => {
      const updates: Record<string, unknown> = {
        updatedAt: new Date(),
      };
      if (status !== undefined) updates.status = status;
      if (priority !== undefined) updates.priority = priority;
      if (title !== undefined) updates.title = title;
      if (description !== undefined) updates.description = description;

      const [updated] = await db
        .update(task)
        .set(updates)
        .where(and(eq(task.id, id), eq(task.userId, userId)))
        .returning();

      if (!updated) {
        throw new Error(`Task ${id} not found or access denied`);
      }

      return {
        id: updated.id,
        title: updated.title,
        description: updated.description,
        status: updated.status,
        priority: updated.priority,
        createdAt: updated.createdAt,
        updatedAt: updated.updatedAt,
      };
    },
  });

  const listTasks = tool({
    description:
      "List the user's current tasks. Use this when the user asks about their tasks, to-do list, or what they need to do.",
    inputSchema: z.object({
      status: z
        .enum(["todo", "in_progress", "done", "cancelled", "all"])
        .default("all")
        .describe("Filter by status. Use 'all' to show everything."),
    }),
    execute: async ({ status }): Promise<TaskListResult> => {
      const conditions = [eq(task.userId, userId)];

      if (status !== "all") {
        conditions.push(eq(task.status, status));
      }

      const tasks = await db
        .select()
        .from(task)
        .where(and(...conditions))
        .orderBy(task.sortOrder);

      return {
        tasks: tasks.map((t) => ({
          id: t.id,
          title: t.title,
          description: t.description,
          status: t.status,
          priority: t.priority,
          createdAt: t.createdAt,
          updatedAt
Files: 1
Size: 26.8 KB
Complexity: 36/100
Category: Design

Related in Design