@@ -4,6 +4,36 @@ import { skills, ratings, reviews, favorites } from "~/lib/db/schema";
44import { eq , desc , count , avg } from "drizzle-orm" ;
55import { getSession } from "~/lib/auth/session-helpers" ;
66
7+ /** Detect stub content: short + ends with "## Author\n{author}" */
8+ function isStubContent ( content : string , author : string ) : boolean {
9+ return content . length < 300 && content . trimEnd ( ) . endsWith ( `## Author\n${ author } ` ) ;
10+ }
11+
12+ /** Derive raw SKILL.md URL from a GitHub source_url like
13+ * https://github.com/{owner}/{repo}/tree/{branch}/{path} */
14+ function toRawSkillMdUrl ( sourceUrl : string ) : string | null {
15+ const m = sourceUrl . match ( / g i t h u b \. c o m \/ ( [ ^ / ] + ) \/ ( [ ^ / ] + ) \/ t r e e \/ ( [ ^ / ] + ) \/ ( .* ) / ) ;
16+ if ( ! m ) return null ;
17+ return `https://raw.githubusercontent.com/${ m [ 1 ] } /${ m [ 2 ] } /${ m [ 3 ] } /${ m [ 4 ] } /SKILL.md` ;
18+ }
19+
20+ /** Fetch real SKILL.md content from GitHub. Returns null on failure. */
21+ async function fetchRealContent ( sourceUrl : string ) : Promise < string | null > {
22+ const rawUrl = toRawSkillMdUrl ( sourceUrl ) ;
23+ if ( ! rawUrl ) return null ;
24+
25+ try {
26+ const res = await fetch ( rawUrl , {
27+ headers : { "User-Agent" : "SkillX/1.0" } ,
28+ } ) ;
29+ if ( ! res . ok ) return null ;
30+ const text = await res . text ( ) ;
31+ return text . trim ( ) . length > 20 ? text : null ;
32+ } catch {
33+ return null ;
34+ }
35+ }
36+
737export async function loader ( { params, request, context } : LoaderFunctionArgs ) {
838 try {
939 const slug = params . slug ;
@@ -25,6 +55,19 @@ export async function loader({ params, request, context }: LoaderFunctionArgs) {
2555 return Response . json ( { error : "Skill not found" } , { status : 404 } ) ;
2656 }
2757
58+ // Lazy content fetch: if DB has stub content, pull real SKILL.md from GitHub
59+ if ( skill . source_url && isStubContent ( skill . content , skill . author ) ) {
60+ const realContent = await fetchRealContent ( skill . source_url ) ;
61+ if ( realContent ) {
62+ skill . content = realContent ;
63+ // Persist to DB so future requests are fast (fire-and-forget)
64+ db . update ( skills )
65+ . set ( { content : realContent , updated_at : new Date ( ) } )
66+ . where ( eq ( skills . id , skill . id ) )
67+ . catch ( ( ) => { } ) ;
68+ }
69+ }
70+
2871 // Fetch reviews with limit
2972 const skillReviews = await db
3073 . select ( )
0 commit comments