import { v } from "convex/values"; import { query, internalQuery } from "./_generated/server"; import { bugSeverityValidator, bugStatusValidator, feedbackTypeValidator, feedbackPriorityValidator, feedbackStatusValidator, effortValidator, } from "./schema"; // ===== Template Types ===== export type PromptTemplate = "fix" | "implement" | "analyze" | "codebuff"; export const promptTemplateValidator = v.union( v.literal("fix"), v.literal("implement"), v.literal("analyze"), v.literal("codebuff") ); // ===== Bug Report Type ===== const bugReportDataValidator = v.object({ _id: v.id("bugReports"), ticketNumber: v.string(), title: v.string(), description: v.string(), severity: bugSeverityValidator, status: bugStatusValidator, url: v.string(), route: v.optional(v.string()), browserInfo: v.string(), consoleErrors: v.optional(v.string()), viewportWidth: v.number(), viewportHeight: v.number(), networkState: v.string(), aiSummary: v.optional(v.string()), aiRootCauseAnalysis: v.optional(v.string()), aiReproductionSteps: v.optional(v.array(v.string())), aiSuggestedFix: v.optional(v.string()), aiEstimatedEffort: v.optional(effortValidator), createdAt: v.number(), }); // ===== Feedback Type ===== const feedbackDataValidator = v.object({ _id: v.id("feedback"), ticketNumber: v.string(), type: feedbackTypeValidator, title: v.string(), description: v.string(), priority: feedbackPriorityValidator, status: feedbackStatusValidator, url: v.string(), route: v.optional(v.string()), browserInfo: v.string(), consoleErrors: v.optional(v.string()), viewportWidth: v.number(), viewportHeight: v.number(), networkState: v.string(), aiSummary: v.optional(v.string()), aiImpactAnalysis: v.optional(v.string()), aiActionItems: v.optional(v.array(v.string())), aiEstimatedEffort: v.optional(effortValidator), createdAt: v.number(), }); // ===== Prompt Generation ===== function formatDate(timestamp: number): string { return new Date(timestamp).toISOString().split("T")[0]; } function parseBrowserInfo(browserInfoJson: string): Record { try { return JSON.parse(browserInfoJson); } catch { return { raw: browserInfoJson }; } } function parseConsoleErrors(consoleErrorsJson: string | undefined): unknown[] { if (!consoleErrorsJson) return []; try { return JSON.parse(consoleErrorsJson); } catch { return [consoleErrorsJson]; } } // ===== Bug Report Prompts ===== function generateBugFixPrompt(bug: { ticketNumber: string; title: string; description: string; severity: string; url: string; route?: string; browserInfo: string; consoleErrors?: string; viewportWidth: number; viewportHeight: number; aiSummary?: string; aiRootCauseAnalysis?: string; aiReproductionSteps?: string[]; aiSuggestedFix?: string; aiEstimatedEffort?: string; createdAt: number; }): string { const browserInfo = parseBrowserInfo(bug.browserInfo); const consoleErrors = parseConsoleErrors(bug.consoleErrors); let prompt = `# Bug Fix Request: ${bug.ticketNumber} ## Issue Summary **Title:** ${bug.title} **Severity:** ${bug.severity.toUpperCase()} **Reported:** ${formatDate(bug.createdAt)} **URL:** ${bug.url}${bug.route ? `\n**Route:** ${bug.route}` : ""} ## Description ${bug.description} `; if (bug.aiSummary) { prompt += `\n## AI Summary\n${bug.aiSummary}\n`; } if (bug.aiRootCauseAnalysis) { prompt += `\n## Root Cause Analysis\n${bug.aiRootCauseAnalysis}\n`; } if (bug.aiReproductionSteps && bug.aiReproductionSteps.length > 0) { prompt += `\n## Steps to Reproduce\n${bug.aiReproductionSteps.map((s, i) => `${i + 1}. ${s}`).join("\n")}\n`; } if (bug.aiSuggestedFix) { prompt += `\n## Suggested Fix\n${bug.aiSuggestedFix}\n`; } if (consoleErrors.length > 0) { prompt += `\n## Console Errors\n\`\`\`\n${JSON.stringify(consoleErrors, null, 2)}\n\`\`\`\n`; } prompt += `\n## Environment\n- **Browser:** ${browserInfo.userAgent || "Unknown"}\n- **Viewport:** ${bug.viewportWidth}x${bug.viewportHeight}\n`; if (bug.aiEstimatedEffort) { prompt += `\n## Estimated Effort: ${bug.aiEstimatedEffort.toUpperCase()}\n`; } prompt += `\n## Instructions\nPlease fix the bug described above. Focus on:\n1. Identifying the root cause in the codebase\n2. Implementing a fix that addresses the issue\n3. Ensuring the fix doesn't introduce regressions\n4. Adding appropriate error handling if needed\n`; return prompt; } function generateBugAnalyzePrompt(bug: { ticketNumber: string; title: string; description: string; severity: string; url: string; route?: string; browserInfo: string; consoleErrors?: string; viewportWidth: number; viewportHeight: number; aiSummary?: string; createdAt: number; }): string { const browserInfo = parseBrowserInfo(bug.browserInfo); const consoleErrors = parseConsoleErrors(bug.consoleErrors); let prompt = `# Deep Analysis Request: ${bug.ticketNumber} ## Bug Report **Title:** ${bug.title} **Severity:** ${bug.severity.toUpperCase()} **Reported:** ${formatDate(bug.createdAt)} **URL:** ${bug.url}${bug.route ? `\n**Route:** ${bug.route}` : ""} ## Description ${bug.description} `; if (consoleErrors.length > 0) { prompt += `\n## Console Errors\n\`\`\`json\n${JSON.stringify(consoleErrors, null, 2)}\n\`\`\`\n`; } prompt += `\n## Environment\n\`\`\`json\n${JSON.stringify(browserInfo, null, 2)}\n\`\`\`\n**Viewport:** ${bug.viewportWidth}x${bug.viewportHeight}\n`; prompt += `\n## Analysis Instructions\nPlease perform a deep analysis of this bug report:\n\n1. **Root Cause Analysis**: What is likely causing this issue?\n2. **Impact Assessment**: What parts of the application could be affected?\n3. **Reproduction Steps**: Based on the context, what steps would reproduce this?\n4. **Fix Strategy**: What approach would you recommend to fix this?\n5. **Risk Assessment**: What are the risks of the proposed fix?\n6. **Testing Requirements**: What tests should be added or updated?\n`; return prompt; } function generateBugCodebuffPrompt(bug: { ticketNumber: string; title: string; description: string; severity: string; url: string; route?: string; browserInfo: string; consoleErrors?: string; viewportWidth: number; viewportHeight: number; aiSummary?: string; aiRootCauseAnalysis?: string; aiReproductionSteps?: string[]; aiSuggestedFix?: string; createdAt: number; }): string { const consoleErrors = parseConsoleErrors(bug.consoleErrors); let prompt = `Fix bug ${bug.ticketNumber}: ${bug.title}\n\n`; prompt += `**Severity:** ${bug.severity}\n`; prompt += `**URL:** ${bug.url}\n`; if (bug.route) prompt += `**Route:** ${bug.route}\n`; prompt += `\n${bug.description}\n`; if (bug.aiRootCauseAnalysis) { prompt += `\n**Root Cause:** ${bug.aiRootCauseAnalysis}\n`; } if (bug.aiSuggestedFix) { prompt += `\n**Suggested Fix:** ${bug.aiSuggestedFix}\n`; } if (consoleErrors.length > 0) { prompt += `\n**Console Errors:**\n\`\`\`\n${JSON.stringify(consoleErrors, null, 2)}\n\`\`\`\n`; } return prompt; } // ===== Feedback Prompts ===== function generateFeedbackImplementPrompt(feedback: { ticketNumber: string; type: string; title: string; description: string; priority: string; url: string; route?: string; aiSummary?: string; aiImpactAnalysis?: string; aiActionItems?: string[]; aiEstimatedEffort?: string; createdAt: number; }): string { const typeLabel = feedback.type.replace("_", " ").replace(/\b\w/g, (l) => l.toUpperCase()); let prompt = `# Implementation Request: ${feedback.ticketNumber} ## Request Summary **Title:** ${feedback.title} **Type:** ${typeLabel} **Priority:** ${feedback.priority.replace("_", " ").toUpperCase()} **Requested:** ${formatDate(feedback.createdAt)} **Context URL:** ${feedback.url}${feedback.route ? `\n**Route:** ${feedback.route}` : ""} ## Description ${feedback.description} `; if (feedback.aiSummary) { prompt += `\n## Summary\n${feedback.aiSummary}\n`; } if (feedback.aiImpactAnalysis) { prompt += `\n## Impact Analysis\n${feedback.aiImpactAnalysis}\n`; } if (feedback.aiActionItems && feedback.aiActionItems.length > 0) { prompt += `\n## Action Items\n${feedback.aiActionItems.map((item, i) => `${i + 1}. ${item}`).join("\n")}\n`; } if (feedback.aiEstimatedEffort) { prompt += `\n## Estimated Effort: ${feedback.aiEstimatedEffort.toUpperCase()}\n`; } prompt += `\n## Implementation Instructions\nPlease implement this ${typeLabel.toLowerCase()}. Focus on:\n1. Understanding the user's need and use case\n2. Designing a solution that fits the existing architecture\n3. Implementing the feature with proper error handling\n4. Adding appropriate tests\n5. Updating documentation if needed\n`; return prompt; } function generateFeedbackAnalyzePrompt(feedback: { ticketNumber: string; type: string; title: string; description: string; priority: string; url: string; route?: string; aiSummary?: string; createdAt: number; }): string { const typeLabel = feedback.type.replace("_", " ").replace(/\b\w/g, (l) => l.toUpperCase()); let prompt = `# Analysis Request: ${feedback.ticketNumber} ## Feedback Details **Title:** ${feedback.title} **Type:** ${typeLabel} **Priority:** ${feedback.priority.replace("_", " ").toUpperCase()} **Submitted:** ${formatDate(feedback.createdAt)} **Context URL:** ${feedback.url}${feedback.route ? `\n**Route:** ${feedback.route}` : ""} ## Description ${feedback.description} `; prompt += `\n## Analysis Instructions\nPlease analyze this ${typeLabel.toLowerCase()} and provide:\n\n1. **Feasibility Assessment**: Is this technically feasible? What are the constraints?\n2. **Impact Analysis**: How would this affect the existing system?\n3. **Effort Estimation**: How much work would this require?\n4. **Alternative Approaches**: Are there simpler ways to achieve the user's goal?\n5. **Dependencies**: What other features or systems would this depend on?\n6. **Risks**: What could go wrong with this implementation?\n`; return prompt; } function generateFeedbackCodebuffPrompt(feedback: { ticketNumber: string; type: string; title: string; description: string; priority: string; url: string; route?: string; aiSummary?: string; aiActionItems?: string[]; createdAt: number; }): string { const typeLabel = feedback.type === "feature_request" ? "feature" : feedback.type === "change_request" ? "change" : "feedback"; let prompt = `Implement ${typeLabel} ${feedback.ticketNumber}: ${feedback.title}\n\n`; prompt += `**Priority:** ${feedback.priority.replace("_", " ")}\n`; prompt += `**URL:** ${feedback.url}\n`; if (feedback.route) prompt += `**Route:** ${feedback.route}\n`; prompt += `\n${feedback.description}\n`; if (feedback.aiActionItems && feedback.aiActionItems.length > 0) { prompt += `\n**Action Items:**\n${feedback.aiActionItems.map((item) => `- ${item}`).join("\n")}\n`; } return prompt; } // ===== Public API ===== /** * Generate an AI prompt for a bug report. */ export function generateBugPrompt( bug: { ticketNumber: string; title: string; description: string; severity: string; url: string; route?: string; browserInfo: string; consoleErrors?: string; viewportWidth: number; viewportHeight: number; aiSummary?: string; aiRootCauseAnalysis?: string; aiReproductionSteps?: string[]; aiSuggestedFix?: string; aiEstimatedEffort?: string; createdAt: number; }, template: PromptTemplate ): string { switch (template) { case "fix": return generateBugFixPrompt(bug); case "analyze": return generateBugAnalyzePrompt(bug); case "codebuff": return generateBugCodebuffPrompt(bug); case "implement": // For bugs, "implement" falls back to "fix" return generateBugFixPrompt(bug); } } /** * Generate an AI prompt for feedback. */ export function generateFeedbackPrompt( feedback: { ticketNumber: string; type: string; title: string; description: string; priority: string; url: string; route?: string; aiSummary?: string; aiImpactAnalysis?: string; aiActionItems?: string[]; aiEstimatedEffort?: string; createdAt: number; }, template: PromptTemplate ): string { switch (template) { case "implement": return generateFeedbackImplementPrompt(feedback); case "analyze": return generateFeedbackAnalyzePrompt(feedback); case "codebuff": return generateFeedbackCodebuffPrompt(feedback); case "fix": // For feedback, "fix" falls back to "implement" return generateFeedbackImplementPrompt(feedback); } } /** * Get a bug report by ticket number. * Used by consumer apps via component API and HTTP API. */ export const getBugByTicketNumber = query({ args: { ticketNumber: v.string(), }, returns: v.union(bugReportDataValidator, v.null()), handler: async (ctx, args) => { const bug = await ctx.db .query("bugReports") .withIndex("by_ticket_number", (q) => q.eq("ticketNumber", args.ticketNumber)) .unique(); if (!bug || !bug.ticketNumber) { return null; } return { _id: bug._id, ticketNumber: bug.ticketNumber, title: bug.title, description: bug.description, severity: bug.severity, status: bug.status, url: bug.url, route: bug.route, browserInfo: bug.browserInfo, consoleErrors: bug.consoleErrors, viewportWidth: bug.viewportWidth, viewportHeight: bug.viewportHeight, networkState: bug.networkState, aiSummary: bug.aiSummary, aiRootCauseAnalysis: bug.aiRootCauseAnalysis, aiReproductionSteps: bug.aiReproductionSteps, aiSuggestedFix: bug.aiSuggestedFix, aiEstimatedEffort: bug.aiEstimatedEffort, createdAt: bug.createdAt, }; }, }); /** * Get feedback by ticket number. * Used by consumer apps via component API and HTTP API. */ export const getFeedbackByTicketNumber = query({ args: { ticketNumber: v.string(), }, returns: v.union(feedbackDataValidator, v.null()), handler: async (ctx, args) => { const feedback = await ctx.db .query("feedback") .withIndex("by_ticket_number", (q) => q.eq("ticketNumber", args.ticketNumber)) .unique(); if (!feedback || !feedback.ticketNumber) { return null; } return { _id: feedback._id, ticketNumber: feedback.ticketNumber, type: feedback.type, title: feedback.title, description: feedback.description, priority: feedback.priority, status: feedback.status, url: feedback.url, route: feedback.route, browserInfo: feedback.browserInfo, consoleErrors: feedback.consoleErrors, viewportWidth: feedback.viewportWidth, viewportHeight: feedback.viewportHeight, networkState: feedback.networkState, aiSummary: feedback.aiSummary, aiImpactAnalysis: feedback.aiImpactAnalysis, aiActionItems: feedback.aiActionItems, aiEstimatedEffort: feedback.aiEstimatedEffort, createdAt: feedback.createdAt, }; }, }); /** * Public query to get a prompt by ticket number. */ export const getPrompt = query({ args: { ticketNumber: v.string(), template: v.optional(promptTemplateValidator), }, returns: v.union( v.object({ ticketNumber: v.string(), type: v.union(v.literal("bug"), v.literal("feedback")), template: promptTemplateValidator, prompt: v.string(), }), v.null() ), handler: async (ctx, args) => { const ticketNumber = args.ticketNumber.toUpperCase(); const isBug = ticketNumber.startsWith("BUG-"); const isFeedback = ticketNumber.startsWith("FB-"); if (!isBug && !isFeedback) { return null; } if (isBug) { const bug = await ctx.db .query("bugReports") .withIndex("by_ticket_number", (q) => q.eq("ticketNumber", ticketNumber)) .unique(); if (!bug || !bug.ticketNumber) { return null; } const template = args.template ?? "fix"; return { ticketNumber: bug.ticketNumber, type: "bug" as const, template, prompt: generateBugPrompt( { ticketNumber: bug.ticketNumber, title: bug.title, description: bug.description, severity: bug.severity, url: bug.url, route: bug.route, browserInfo: bug.browserInfo, consoleErrors: bug.consoleErrors, viewportWidth: bug.viewportWidth, viewportHeight: bug.viewportHeight, aiSummary: bug.aiSummary, aiRootCauseAnalysis: bug.aiRootCauseAnalysis, aiReproductionSteps: bug.aiReproductionSteps, aiSuggestedFix: bug.aiSuggestedFix, aiEstimatedEffort: bug.aiEstimatedEffort, createdAt: bug.createdAt, }, template ), }; } // Feedback const feedback = await ctx.db .query("feedback") .withIndex("by_ticket_number", (q) => q.eq("ticketNumber", ticketNumber)) .unique(); if (!feedback || !feedback.ticketNumber) { return null; } const template = args.template ?? "implement"; return { ticketNumber: feedback.ticketNumber, type: "feedback" as const, template, prompt: generateFeedbackPrompt( { ticketNumber: feedback.ticketNumber, type: feedback.type, title: feedback.title, description: feedback.description, priority: feedback.priority, url: feedback.url, route: feedback.route, aiSummary: feedback.aiSummary, aiImpactAnalysis: feedback.aiImpactAnalysis, aiActionItems: feedback.aiActionItems, aiEstimatedEffort: feedback.aiEstimatedEffort, createdAt: feedback.createdAt, }, template ), }; }, });