/ opencodehub / src / db / schema / ai-review-rules.ts
swadhinbiswas Fix: Add date validation in analysis.ts to prevent RangeError · 1/20/2026
a7662cd
127 lines · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
 * AI Review Rules Schema - Drizzle ORM
 * Custom AI review rules with prompts and patterns
 */

import { relations } from "drizzle-orm";
import { boolean, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { repositories } from "./repositories";
import { users } from "./users";

/**
 * Custom AI review rules for repository-specific reviews
 * Similar to Graphite Agent's custom rules feature
 */
export const aiReviewRules = pgTable("ai_review_rules", {
    id: text("id").primaryKey(),
    repositoryId: text("repository_id")
        .references(() => repositories.id, { onDelete: "cascade" }),
    organizationId: text("organization_id"), // Optional org-wide rules
    createdById: text("created_by_id")
        .notNull()
        .references(() => users.id),
    name: text("name").notNull(),
    description: text("description"),

    // Rule definition
    ruleType: text("rule_type").notNull().default("ai_prompt"), // ai_prompt, regex, both
    aiPrompt: text("ai_prompt"), // Custom AI prompt for this rule
    regexPattern: text("regex_pattern"), // Regex pattern to match

    // Severity and categorization
    severity: text("severity").notNull().default("info"), // info, warning, error, critical
    category: text("category"), // security, performance, style, bug, documentation

    // Scope
    fileGlobs: text("file_globs"), // JSON array of glob patterns to include
    excludeGlobs: text("exclude_globs"), // JSON array of glob patterns to exclude
    languages: text("languages"), // JSON array of languages to apply to

    // Behavior
    isEnabled: boolean("is_enabled").default(true),
    isAutoFix: boolean("is_auto_fix").default(false), // Can auto-fix
    priority: integer("priority").default(0), // Higher = runs first

    // Stats
    matchCount: integer("match_count").default(0),
    lastMatchAt: timestamp("last_match_at"),

    createdAt: timestamp("created_at").notNull().defaultNow(),
    updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

/**
 * Pre-built rule templates that users can import
 */
export const aiReviewRuleTemplates = pgTable("ai_review_rule_templates", {
    id: text("id").primaryKey(),
    name: text("name").notNull(),
    description: text("description"),
    ruleType: text("rule_type").notNull(),
    aiPrompt: text("ai_prompt"),
    regexPattern: text("regex_pattern"),
    severity: text("severity").notNull(),
    category: text("category"),
    fileGlobs: text("file_globs"),
    languages: text("languages"),
    isBuiltIn: boolean("is_built_in").default(false),
    usageCount: integer("usage_count").default(0),
    createdAt: timestamp("created_at").notNull().defaultNow(),
});

/**
 * Codebase context for AI reviews - stores patterns and conventions
 */
export const aiCodebaseContext = pgTable("ai_codebase_context", {
    id: text("id").primaryKey(),
    repositoryId: text("repository_id")
        .notNull()
        .references(() => repositories.id, { onDelete: "cascade" }),
    contextType: text("context_type").notNull(), // naming_convention, pattern, architecture, etc.
    name: text("name").notNull(),
    description: text("description"),
    examples: text("examples"), // JSON array of code examples
    antiPatterns: text("anti_patterns"), // JSON array of things to avoid
    isAutoDetected: boolean("is_auto_detected").default(false),
    confidence: integer("confidence"), // 0-100 if auto-detected
    createdAt: timestamp("created_at").notNull().defaultNow(),
    updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

// Relations
export const aiReviewRulesRelations = relations(aiReviewRules, ({ one }) => ({
    repository: one(repositories, {
        fields: [aiReviewRules.repositoryId],
        references: [repositories.id],
    }),
    createdBy: one(users, {
        fields: [aiReviewRules.createdById],
        references: [users.id],
    }),
}));

export const aiCodebaseContextRelations = relations(aiCodebaseContext, ({ one }) => ({
    repository: one(repositories, {
        fields: [aiCodebaseContext.repositoryId],
        references: [repositories.id],
    }),
}));

// Types
export type AIReviewRule = typeof aiReviewRules.$inferSelect;
export type AIReviewRuleTemplate = typeof aiReviewRuleTemplates.$inferSelect;
export type AICodebaseContext = typeof aiCodebaseContext.$inferSelect;

export interface AIReviewRuleConfig {
    name: string;
    description?: string;
    ruleType: "ai_prompt" | "regex" | "both";
    aiPrompt?: string;
    regexPattern?: string;
    severity: "info" | "warning" | "error" | "critical";
    category?: "security" | "performance" | "style" | "bug" | "documentation";
    fileGlobs?: string[];
    excludeGlobs?: string[];
    languages?: string[];
}