@@ -19,12 +19,82 @@ export const config = {
1919
2020// This function can be marked `async` if using `await` inside
2121export function middleware ( request : NextRequest ) {
22+ // Check for AI/LLM clients and redirect to markdown if appropriate
23+ const markdownRedirect = handleAIClientRedirect ( request ) ;
24+ if ( markdownRedirect ) {
25+ return markdownRedirect ;
26+ }
27+
2228 return handleRedirects ( request ) ;
2329}
2430
2531// don't send Permanent Redirects (301) in dev mode - it gets cached for "localhost" by the browser
2632const redirectStatusCode = process . env . NODE_ENV === 'development' ? 302 : 301 ;
2733
34+ /**
35+ * Detects if the user agent belongs to an AI/LLM tool or development environment
36+ * that would benefit from markdown format
37+ */
38+ function isAIOrDevTool ( userAgent : string ) : boolean {
39+ const patterns = [
40+ / c l a u d e / i, // Claude Desktop/Code
41+ / c u r s o r / i, // Cursor IDE
42+ / c o p i l o t / i, // GitHub Copilot
43+ / c h a t g p t / i, // ChatGPT
44+ / o p e n a i / i, // OpenAI tools
45+ / a n t h r o p i c / i, // Anthropic tools
46+ / v s c o d e / i, // VS Code extensions
47+ / i n t e l l i j / i, // IntelliJ plugins
48+ / s u b l i m e / i, // Sublime Text plugins
49+ // Add more patterns as needed
50+ ] ;
51+
52+ return patterns . some ( pattern => pattern . test ( userAgent ) ) ;
53+ }
54+
55+ /**
56+ * Handles redirection to markdown versions for AI/LLM clients
57+ */
58+ const handleAIClientRedirect = ( request : NextRequest ) => {
59+ const userAgent = request . headers . get ( 'user-agent' ) || '' ;
60+ const url = request . nextUrl ;
61+
62+ // Skip if already requesting a markdown file
63+ if ( url . pathname . endsWith ( '.md' ) ) {
64+ return undefined ;
65+ }
66+
67+ // Skip API routes and static assets (should already be filtered by matcher)
68+ if ( url . pathname . startsWith ( '/api/' ) ||
69+ url . pathname . startsWith ( '/_next/' ) ||
70+ url . pathname . includes ( '.' ) ) {
71+ return undefined ;
72+ }
73+
74+ // Check for explicit format request via query parameter
75+ const forceMarkdown = url . searchParams . get ( 'format' ) === 'md' ;
76+ const isAIClient = isAIOrDevTool ( userAgent ) ;
77+
78+ if ( isAIClient || forceMarkdown ) {
79+ // Create new URL with .md extension
80+ const newUrl = url . clone ( ) ;
81+ // Handle root path and ensure proper .md extension
82+ const pathname = url . pathname === '/' ? '/index' : url . pathname . replace ( / \/ $ / , '' ) ;
83+ newUrl . pathname = pathname + '.md' ;
84+
85+ // Clean up the format query parameter if it was used
86+ if ( forceMarkdown ) {
87+ newUrl . searchParams . delete ( 'format' ) ;
88+ }
89+
90+ return NextResponse . redirect ( newUrl , {
91+ status : redirectStatusCode ,
92+ } ) ;
93+ }
94+
95+ return undefined ;
96+ } ;
97+
2898const handleRedirects = ( request : NextRequest ) => {
2999 const urlPath = request . nextUrl . pathname ;
30100
0 commit comments