import { pgTable, uuid, text, integer, timestamp, jsonb, pgEnum, boolean, } from "drizzle-orm/pg-core"; export const taskStatusEnum = pgEnum("task_status", [ "active", "queued", "blocked", "completed", "cancelled", ]); export const taskPriorityEnum = pgEnum("task_priority", [ "critical", "high", "medium", "low", ]); export const taskSourceEnum = pgEnum("task_source", [ "donovan", "david", "hammer", "heartbeat", "cron", "other", ]); export interface ProgressNote { timestamp: string; note: string; } export interface Subtask { id: string; title: string; completed: boolean; completedAt?: string; createdAt: string; } export type RecurrenceFrequency = "daily" | "weekly" | "biweekly" | "monthly"; export interface Recurrence { frequency: RecurrenceFrequency; /** Auto-activate the next instance (vs. queue it) */ autoActivate?: boolean; } // ─── Projects ─── export interface ProjectLink { label: string; url: string; } export const projects = pgTable("projects", { id: uuid("id").defaultRandom().primaryKey(), name: text("name").notNull(), description: text("description"), context: text("context"), // Architecture notes, how-to, credentials references repos: jsonb("repos").$type().default([]), // Git repo URLs links: jsonb("links").$type().default([]), // Related URLs (docs, domains, dashboards) createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }); export type Project = typeof projects.$inferSelect; export type NewProject = typeof projects.$inferInsert; // ─── Tasks ─── export const tasks = pgTable("tasks", { id: uuid("id").defaultRandom().primaryKey(), taskNumber: integer("task_number"), title: text("title").notNull(), description: text("description"), source: taskSourceEnum("source").notNull().default("donovan"), status: taskStatusEnum("status").notNull().default("queued"), priority: taskPriorityEnum("priority").notNull().default("medium"), position: integer("position").notNull().default(0), assigneeId: text("assignee_id"), assigneeName: text("assignee_name"), projectId: uuid("project_id").references(() => projects.id, { onDelete: "set null" }), dueDate: timestamp("due_date", { withTimezone: true }), estimatedHours: integer("estimated_hours"), tags: jsonb("tags").$type().default([]), recurrence: jsonb("recurrence").$type(), subtasks: jsonb("subtasks").$type().default([]), progressNotes: jsonb("progress_notes").$type().default([]), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), completedAt: timestamp("completed_at", { withTimezone: true }), }); export type Task = typeof tasks.$inferSelect; export type NewTask = typeof tasks.$inferInsert; // ─── Comments ─── export const taskComments = pgTable("task_comments", { id: uuid("id").defaultRandom().primaryKey(), taskId: uuid("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }), authorId: text("author_id"), // BetterAuth user ID, or "hammer" for API, null for anonymous authorName: text("author_name").notNull(), content: text("content").notNull(), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), }); export type TaskComment = typeof taskComments.$inferSelect; export type NewTaskComment = typeof taskComments.$inferInsert; // ─── Security Audits ─── export const securityAuditStatusEnum = pgEnum("security_audit_status", [ "strong", "needs_improvement", "critical", ]); export interface SecurityFinding { id: string; status: "strong" | "needs_improvement" | "critical"; title: string; description: string; recommendation: string; } export const securityAudits = pgTable("security_audits", { id: uuid("id").defaultRandom().primaryKey(), projectName: text("project_name").notNull(), category: text("category").notNull(), findings: jsonb("findings").$type().default([]), score: integer("score").notNull().default(0), // 0-100 lastAudited: timestamp("last_audited", { withTimezone: true }).defaultNow().notNull(), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }); export type SecurityAudit = typeof securityAudits.$inferSelect; export type NewSecurityAudit = typeof securityAudits.$inferInsert; // ─── Daily Summaries ─── export interface SummaryHighlight { text: string; } export interface SummaryStats { deploys?: number; commits?: number; tasksCompleted?: number; featuresBuilt?: number; bugsFixed?: number; [key: string]: number | undefined; } export const dailySummaries = pgTable("daily_summaries", { id: uuid("id").defaultRandom().primaryKey(), date: text("date").notNull().unique(), // YYYY-MM-DD content: text("content").notNull(), highlights: jsonb("highlights").$type().default([]), stats: jsonb("stats").$type().default({}), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }); export type DailySummary = typeof dailySummaries.$inferSelect; export type NewDailySummary = typeof dailySummaries.$inferInsert; // ─── Personal Todos ─── export const todoPriorityEnum = pgEnum("todo_priority", [ "high", "medium", "low", "none", ]); export const todos = pgTable("todos", { id: uuid("id").defaultRandom().primaryKey(), userId: text("user_id").notNull(), title: text("title").notNull(), description: text("description"), isCompleted: boolean("is_completed").notNull().default(false), priority: todoPriorityEnum("priority").notNull().default("none"), category: text("category"), dueDate: timestamp("due_date", { withTimezone: true }), completedAt: timestamp("completed_at", { withTimezone: true }), sortOrder: integer("sort_order").notNull().default(0), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), }); export type Todo = typeof todos.$inferSelect; export type NewTodo = typeof todos.$inferInsert; // ─── BetterAuth tables ─── export const users = pgTable("users", { id: text("id").primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").notNull().default(false), image: text("image"), role: text("role").notNull().default("user"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }); export const sessions = pgTable("sessions", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), token: text("token").notNull().unique(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: text("user_id").notNull().references(() => users.id), }); export const accounts = pgTable("accounts", { id: text("id").primaryKey(), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: text("user_id").notNull().references(() => users.id), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), accessTokenExpiresAt: timestamp("access_token_expires_at", { withTimezone: true }), refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { withTimezone: true }), scope: text("scope"), password: text("password"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }); export const verifications = pgTable("verifications", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), });