@@ -2,11 +2,13 @@ import { NextResponse } from "next/server";
22import { getGeminiModel } from "@/lib/gemini" ;
33import { getRepoData , getRepoContents } from "@/lib/octokit" ;
44
5- // Ensure API keys are read at runtime
65export const dynamic = "force-dynamic" ;
76
7+ /**
8+ * AI README Generation Endpoint
9+ * Optimized for data accuracy and clean prompt interpolation.
10+ */
811export async function POST ( req : Request ) {
9- // 1. Safe JSON Body Parsing
1012 let rawUrl : string ;
1113 try {
1214 const body = await req . json ( ) ;
@@ -16,7 +18,6 @@ export async function POST(req: Request) {
1618 }
1719
1820 try {
19- // 2. Strict URL Validation
2021 const trimmedUrl = rawUrl ?. trim ( ) ;
2122 if ( ! trimmedUrl ) {
2223 return NextResponse . json ( { error : "GitHub URL is required" } , { status : 400 } ) ;
@@ -29,12 +30,10 @@ export async function POST(req: Request) {
2930 return NextResponse . json ( { error : "Please provide a valid URL" } , { status : 400 } ) ;
3031 }
3132
32- // Hostname Guard
3333 if ( parsedUrl . hostname !== "github.com" && parsedUrl . hostname !== "www.github.com" ) {
3434 return NextResponse . json ( { error : "Only GitHub URLs are supported" } , { status : 400 } ) ;
3535 }
3636
37- // Extract Owner and Repo from path
3837 const pathSegments = parsedUrl . pathname . split ( "/" ) . filter ( Boolean ) ;
3938 const owner = pathSegments [ 0 ] ;
4039 const repo = pathSegments [ 1 ] ;
@@ -43,41 +42,81 @@ export async function POST(req: Request) {
4342 return NextResponse . json ( { error : "URL must include owner and repository name" } , { status : 400 } ) ;
4443 }
4544
46- // 3. Parallel Data Fetching
4745 const [ repoInfo , repoContents ] = await Promise . all ( [
4846 getRepoData ( owner , repo ) ,
4947 getRepoContents ( owner , repo )
5048 ] ) ;
5149
52- // 4. Type-Safe File Mapping (Fixes the 'any' linting error)
53- // We define the shape { name: string } inline to satisfy ESLint
54- const fileList = Array . isArray ( repoContents ) && repoContents . length > 0
55- ? repoContents . map ( ( f : { name : string } ) => f . name ) . join ( ", " )
56- : "Standard repository structure" ;
50+ const files = Array . isArray ( repoContents ) ? repoContents . map ( ( f : { name : string } ) => f . name ) : [ ] ;
51+ const fileListString = files . length > 0 ? files . join ( ", " ) : "Standard repository structure" ;
52+
53+ // Tech Stack detection logic
54+ const hasNode = files . includes ( "package.json" ) ;
55+ const hasPython = files . includes ( "requirements.txt" ) || files . includes ( "setup.py" ) ;
56+ const hasDocker = files . includes ( "Dockerfile" ) || files . includes ( "docker-compose.yml" ) ;
57+
58+ // Fix: Cleanly joined Tech Stack labels
59+ const stackLabels = [
60+ hasNode && "Node.js Environment" ,
61+ hasPython && "Python Environment" ,
62+ hasDocker && "Containerized"
63+ ] . filter ( Boolean ) . join ( ", " ) || "Generic Software Environment" ;
64+
65+ // Fix: Dynamic License detection
66+ const licenseName = repoInfo ?. license ?. name || repoInfo ?. license ?. spdx_id || "the repository's license file" ;
5767
58- // 5. Initialize Gemini 2.5
5968 const model = getGeminiModel ( ) ;
6069
61- // 6. The "Expert Prompt" with Fallbacks
70+ // Fix: Prompt updated with neutral fallbacks and dynamic license
6271 const prompt = `
63- You are an expert Technical Writer. Generate a professional README.md for:
64-
65- Name: ${ repo }
66- Description: ${ repoInfo ?. description || "A modern software project." }
67- Primary Language: ${ repoInfo ?. language || "Not specified" }
68- Root Directory Files: ${ fileList }
69-
70- Requirements:
71- - Include professional SVG badges from shields.io.
72- - Create a visual "Directory Structure" section (tree style).
73- - Include "Features", "Installation", and "Usage" sections.
74- - If 'package.json' exists, provide Node.js installation steps.
75- - Ensure a welcoming, professional developer-centric tone.
76-
77- Return ONLY the Markdown content.
72+ **Role**: You are a Principal Solutions Architect and World-Class Technical Writer.
73+ **Task**: Generate a professional, high-conversion README.md for the GitHub repository: "${ repo } ".
74+
75+ ---
76+ ### 1. PROJECT CONTEXT (VERIFIED DATA)
77+ - **Project Name**: ${ repo }
78+ - **Description**: ${ repoInfo ?. description || "No description provided." }
79+ - **Primary Language**: ${ repoInfo ?. language || "Language unknown" }
80+ - **Detected Root Files**: ${ fileListString }
81+ - **Tech Stack Context**: ${ stackLabels }
82+
83+ ---
84+ ### 2. STRICT README STRUCTURE REQUIREMENTS
85+
86+ 1. **Visual Header**:
87+ - Center-aligned H1 with project name.
88+ - A compelling 1-sentence tagline describing the **Value Proposition**.
89+ - A centered row of Shields.io badges (Build, License, PRs Welcome, Stars).
90+
91+ 2. **The Strategic "Why" (Overview)**:
92+ - **The Problem**: Use a blockquote to describe the real-world pain point this project solves.
93+ - **The Solution**: Explain how this project provides a superior outcome for the user.
94+
95+ 3. **Key Features**:
96+ - Minimum 5 features. Use emojis and focus on **User Benefits**.
97+
98+ 4. **Technical Architecture**:
99+ - Provide a table of the tech stack: | Technology | Purpose | Key Benefit |.
100+ - Create a tree-style directory structure code block using 📁 for folders and 📄 for files based on the file manifest provided.
101+
102+ 5. **Operational Setup**:
103+ - **Prerequisites**: List required runtimes.
104+ - **Installation**: Provide step-by-step terminal commands.
105+ ${ hasNode ? "- Use npm/yarn/pnpm since package.json was detected." : "" }
106+ ${ hasPython ? "- Use pip/venv since Python markers were detected." : "" }
107+ - **Environment**: If any .env or config files are in the manifest, include a configuration section.
108+
109+ 6. **Community & Governance**:
110+ - Professional "Contributing" section (Fork -> Branch -> PR).
111+ - Detailed "License" section: Reference ${ licenseName } and provide a summary of permissions.
112+
113+ ---
114+ ### 3. TONE & STYLE
115+ - **Tone**: Authoritative, polished, and developer-centric.
116+ - **Visuals**: Extensive use of Markdown formatting.
117+ - **Constraint**: Return ONLY the raw Markdown. No conversational filler.
78118 ` ;
79119
80- // 7. AI Generation
81120 const result = await model . generateContent ( prompt ) ;
82121 const response = await result . response ;
83122 const markdown = response . text ( ) ;
0 commit comments