--- import BaseLayout from "@/layouts/BaseLayout.astro"; import { getDatabase, schema } from "@/db"; import { eq, desc } from "drizzle-orm"; import { Bot, Plus, Trash2, Edit2, AlertCircle, AlertTriangle, Info, XOctagon, Code, FileCode, Play, Save, X, } from "lucide-react"; const user = Astro.locals.user; if (!user) { return Astro.redirect("/login"); } const db = getDatabase(); const repoId = Astro.url.searchParams.get("repo") || ""; // Get user's repositories let repositories: { id: string; name: string; fullName?: string }[] = []; try { const repos = await db.query.repositories.findMany({ where: eq(schema.repositories.ownerId, user.id), columns: { id: true, name: true }, with: { owner: { columns: { username: true } } }, }); repositories = repos.map((r) => ({ id: r.id, name: r.name, fullName: `${r.owner.username}/${r.name}`, })); } catch (e) {} // Get AI review rules for selected repo interface AIRule { id: string; name: string; description?: string; ruleType: string; aiPrompt?: string; regexPattern?: string; severity: string; category?: string; fileGlobs?: string; isEnabled: boolean; matchCount: number; } let rules: AIRule[] = []; let templates: { id: string; name: string; description?: string; category?: string; severity: string }[] = []; try { if (repoId) { const dbRules = await db.query.aiReviewRules.findMany({ where: eq(schema.aiReviewRules.repositoryId, repoId), orderBy: [desc(schema.aiReviewRules.createdAt)], }); rules = dbRules.map(r => ({ id: r.id, name: r.name, description: r.description || undefined, ruleType: r.ruleType, aiPrompt: r.aiPrompt || undefined, regexPattern: r.regexPattern || undefined, severity: r.severity, category: r.category || undefined, fileGlobs: r.fileGlobs || undefined, isEnabled: r.isEnabled ?? true, matchCount: r.matchCount || 0, })); } // Get built-in templates const dbTemplates = await db.query.aiReviewRuleTemplates.findMany({ where: eq(schema.aiReviewRuleTemplates.isBuiltIn, true), }); templates = dbTemplates.map(t => ({ id: t.id, name: t.name, description: t.description || undefined, category: t.category || undefined, severity: t.severity, })); } catch (e) { console.error("Failed to fetch AI rules:", e); } function getSeverityIcon(severity: string) { switch (severity) { case "critical": return { icon: XOctagon, color: "text-red-400", bg: "bg-red-500/10" }; case "error": return { icon: AlertCircle, color: "text-orange-400", bg: "bg-orange-500/10" }; case "warning": return { icon: AlertTriangle, color: "text-yellow-400", bg: "bg-yellow-500/10" }; default: return { icon: Info, color: "text-blue-400", bg: "bg-blue-500/10" }; } } function getCategoryLabel(category?: string) { const labels: Record = { security: { label: "Security", color: "text-red-300 bg-red-500/10 border-red-500/30" }, performance: { label: "Performance", color: "text-yellow-300 bg-yellow-500/10 border-yellow-500/30" }, style: { label: "Style", color: "text-purple-300 bg-purple-500/10 border-purple-500/30" }, bug: { label: "Bug", color: "text-orange-300 bg-orange-500/10 border-orange-500/30" }, documentation: { label: "Docs", color: "text-blue-300 bg-blue-500/10 border-blue-500/30" }, }; return labels[category || ""] || { label: category || "General", color: "text-muted-foreground bg-gray-500/10 border-gray-500/30" }; } ---

AI Review Rules

Configure custom rules for AI-powered code reviews

{repoId && ( )}
{!repoId ? (

Select a Repository

Choose a repository to configure AI review rules

) : ( <>
{rules.length === 0 ? (

No Rules Yet

Create custom rules for AI-powered code review

) : (
{rules.map((rule) => { const SeverityIcon = getSeverityIcon(rule.severity).icon; const severityColor = getSeverityIcon(rule.severity).color; const severityBg = getSeverityIcon(rule.severity).bg; const categoryStyle = getCategoryLabel(rule.category); return (
{/* Severity Icon */}
{/* Rule Info */}
{rule.name} {!rule.isEnabled && ( Disabled )} {categoryStyle.label} {rule.ruleType === "ai_prompt" ? "AI Prompt" : rule.ruleType === "regex" ? "Regex" : "AI + Regex"}
{rule.description && (

{rule.description}

)}
{rule.fileGlobs && ( {JSON.parse(rule.fileGlobs).slice(0, 2).join(", ")} {JSON.parse(rule.fileGlobs).length > 2 && "..."} )} {rule.matchCount} matches
{/* Actions */}
); })}
)}
{templates.length > 0 && (

Rule Templates

{templates.map((template) => { const categoryStyle = getCategoryLabel(template.category); return ( ); })}
)} )}