Last active
December 25, 2025 20:10
-
-
Save TKasperczyk/47e58e25e2d74e671981bb3463af2453 to your computer and use it in GitHub Desktop.
CLI tool for deep code review using OpenAI's gpt-5.2-pro with configurable review types (security, architecture, performance, general)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S deno run --allow-read --allow-net --allow-env | |
| import { expandGlob } from "https://deno.land/std@0.224.0/fs/expand_glob.ts"; | |
| const REVIEW_PROMPTS: Record<string, string> = { | |
| general: `You are a senior software engineer conducting a thorough code review. Analyze the provided codebase for: | |
| - Code quality and maintainability | |
| - Potential bugs and edge cases | |
| - Performance issues | |
| - Security vulnerabilities | |
| - Architectural concerns | |
| - Deviations from best practices | |
| Provide specific, actionable feedback with file paths and line references where applicable.`, | |
| security: `You are a security expert conducting a security audit. Focus on: | |
| - Injection vulnerabilities (SQL, XSS, command injection) | |
| - Authentication and authorization flaws | |
| - Data exposure risks | |
| - Insecure dependencies or patterns | |
| - OWASP Top 10 vulnerabilities | |
| - Secrets or credentials in code | |
| Provide severity ratings (Critical/High/Medium/Low) for each finding.`, | |
| architecture: `You are a software architect reviewing the codebase structure. Analyze: | |
| - Overall architecture and design patterns | |
| - Module boundaries and coupling | |
| - Separation of concerns | |
| - Scalability considerations | |
| - Technical debt | |
| - Consistency across the codebase | |
| Provide recommendations for improvements with rationale.`, | |
| performance: `You are a performance engineer reviewing for optimization opportunities. Focus on: | |
| - Algorithmic complexity issues | |
| - Memory leaks and inefficient allocations | |
| - Unnecessary computations or re-renders | |
| - Database query optimization | |
| - Caching opportunities | |
| - Bundle size concerns | |
| Quantify impact where possible.`, | |
| }; | |
| async function loadApiKey(): Promise<string> { | |
| const keyPath = Deno.env.get("GPT_REVIEW_KEY") || | |
| `${Deno.env.get("HOME")}/SUPERSECRETNEVERLOOKHERE/unlimited_tokens_oai.key`; | |
| try { | |
| const key = (await Deno.readTextFile(keyPath)).trim(); | |
| return key; | |
| } catch { | |
| console.error(`Error: Could not read API key from ${keyPath}`); | |
| console.error("Set GPT_REVIEW_KEY env var or place key in ~/Documents/tomeksKingdom.key"); | |
| Deno.exit(1); | |
| } | |
| } | |
| async function gatherFiles(patterns: string[], basePath: string): Promise<Map<string, string>> { | |
| const files = new Map<string, string>(); | |
| for (const pattern of patterns) { | |
| const fullPattern = pattern.startsWith("/") ? pattern : `${basePath}/${pattern}`; | |
| for await (const entry of expandGlob(fullPattern, { globstar: true })) { | |
| if (entry.isFile) { | |
| try { | |
| const content = await Deno.readTextFile(entry.path); | |
| const relativePath = entry.path.replace(basePath + "/", ""); | |
| files.set(relativePath, content); | |
| } catch { | |
| console.error(`Warning: Could not read ${entry.path}`); | |
| } | |
| } | |
| } | |
| } | |
| return files; | |
| } | |
| function formatCodebase(files: Map<string, string>): string { | |
| const parts: string[] = []; | |
| for (const [path, content] of files) { | |
| const ext = path.split(".").pop() || ""; | |
| parts.push(`\`\`\`${ext} ${path}\n${content}\n\`\`\``); | |
| } | |
| return parts.join("\n\n"); | |
| } | |
| function countTokensApprox(text: string): number { | |
| // Rough approximation: ~4 chars per token | |
| return Math.ceil(text.length / 4); | |
| } | |
| async function submitReview( | |
| apiKey: string, | |
| systemPrompt: string, | |
| codebase: string, | |
| userPrompt?: string | |
| ): Promise<void> { | |
| const input = userPrompt | |
| ? `${userPrompt}\n\n---\n\n${codebase}` | |
| : `Please review the following codebase:\n\n${codebase}`; | |
| const body = { | |
| model: "gpt-5.2-pro", | |
| reasoning: { effort: "xhigh" }, | |
| store: false, | |
| instructions: systemPrompt, | |
| input, | |
| }; | |
| console.error("Submitting to gpt-5.2-pro with xhigh reasoning...\n"); | |
| const response = await fetch("https://api.openai.com/v1/responses", { | |
| method: "POST", | |
| headers: { | |
| "Authorization": `Bearer ${apiKey}`, | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify(body), | |
| }); | |
| if (!response.ok) { | |
| const error = await response.json(); | |
| console.error("API Error:", JSON.stringify(error, null, 2)); | |
| Deno.exit(1); | |
| } | |
| const result = await response.json(); | |
| // Extract and print the review | |
| for (const output of result.output) { | |
| if (output.type === "message") { | |
| for (const content of output.content) { | |
| if (content.type === "output_text") { | |
| console.log(content.text); | |
| } | |
| } | |
| } | |
| } | |
| // Print usage stats to stderr | |
| console.error("\n---"); | |
| console.error(`Tokens: ${result.usage.input_tokens} in / ${result.usage.output_tokens} out`); | |
| if (result.usage.output_tokens_details?.reasoning_tokens) { | |
| console.error(`Reasoning tokens: ${result.usage.output_tokens_details.reasoning_tokens}`); | |
| } | |
| } | |
| function printUsage(): void { | |
| console.log(`gpt-review - Submit code to gpt-5.2-pro for deep review | |
| USAGE: | |
| gpt-review [OPTIONS] <PATTERNS...> | |
| OPTIONS: | |
| -t, --type <TYPE> Review type: general, security, architecture, performance | |
| (default: general) | |
| -p, --prompt <TEXT> Custom prompt to prepend to the review request | |
| -d, --dir <PATH> Base directory (default: current directory) | |
| -h, --help Show this help | |
| EXAMPLES: | |
| gpt-review "src/**/*.ts" | |
| gpt-review -t security "src/**/*.ts" "lib/**/*.ts" | |
| gpt-review -t architecture -d /path/to/project "**/*.py" | |
| gpt-review -p "Focus on error handling" "src/api/**/*.ts" | |
| ENVIRONMENT: | |
| GPT_REVIEW_KEY Path to API key file (default: ~/Documents/tomeksKingdom.key) | |
| `); | |
| } | |
| async function main(): Promise<void> { | |
| const args = [...Deno.args]; | |
| if (args.length === 0 || args.includes("-h") || args.includes("--help")) { | |
| printUsage(); | |
| Deno.exit(0); | |
| } | |
| let reviewType = "general"; | |
| let customPrompt: string | undefined; | |
| let baseDir = Deno.cwd(); | |
| const patterns: string[] = []; | |
| let i = 0; | |
| while (i < args.length) { | |
| const arg = args[i]; | |
| if (arg === "-t" || arg === "--type") { | |
| reviewType = args[++i]; | |
| if (!REVIEW_PROMPTS[reviewType]) { | |
| console.error(`Unknown review type: ${reviewType}`); | |
| console.error(`Available: ${Object.keys(REVIEW_PROMPTS).join(", ")}`); | |
| Deno.exit(1); | |
| } | |
| } else if (arg === "-p" || arg === "--prompt") { | |
| customPrompt = args[++i]; | |
| } else if (arg === "-d" || arg === "--dir") { | |
| baseDir = args[++i]; | |
| } else if (!arg.startsWith("-")) { | |
| patterns.push(arg); | |
| } | |
| i++; | |
| } | |
| if (patterns.length === 0) { | |
| console.error("Error: No file patterns provided"); | |
| printUsage(); | |
| Deno.exit(1); | |
| } | |
| console.error(`Review type: ${reviewType}`); | |
| console.error(`Base directory: ${baseDir}`); | |
| console.error(`Patterns: ${patterns.join(", ")}`); | |
| console.error(""); | |
| const apiKey = await loadApiKey(); | |
| const files = await gatherFiles(patterns, baseDir); | |
| if (files.size === 0) { | |
| console.error("No files found matching the patterns"); | |
| Deno.exit(1); | |
| } | |
| console.error(`Found ${files.size} files:`); | |
| for (const path of files.keys()) { | |
| console.error(` ${path}`); | |
| } | |
| console.error(""); | |
| const codebase = formatCodebase(files); | |
| const approxTokens = countTokensApprox(codebase); | |
| console.error(`Approximate input size: ${approxTokens} tokens`); | |
| if (approxTokens > 150000) { | |
| console.error("Warning: Large input may exceed context limits"); | |
| } | |
| await submitReview(apiKey, REVIEW_PROMPTS[reviewType], codebase, customPrompt); | |
| } | |
| main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment