From GitHub Repo to AI-Generated Blog: Shipping a Full Content Pipeline
We just hit a massive milestone: the complete pipeline for turning GitHub project memories into AI-generated blog posts, wrapped in a beautiful, mobile-first UI. Dive into the tech, the triumphs, and the hard-won lessons.
Today marks a significant achievement: we've officially shipped the full GitHub → Memory Import → Blog Generation pipeline! This wasn't just about connecting a few APIs; it involved building a project-based UI from the ground up, ensuring markdown rendering was pristine, and making sure everything felt native and responsive on mobile. After a focused session, the feature set is complete and committed as 49e98f3 on main.
It’s always satisfying to see a complex vision come to life, especially when it involves weaving together so many different threads of functionality. Let’s break down what went into this release.
The Vision: Your Code, Your Content, Automated
The core idea is simple yet powerful: leverage your existing development "memory" (think project notes, documentation snippets, commit messages, or dedicated memory files in a repo) to automatically draft blog posts. This pipeline aims to bridge the gap between development and content creation, making it effortless to share insights from your work.
To achieve this, we needed several key components:
- GitHub Integration: To connect to user repositories and fetch specific files.
- Memory Processing: To identify and ingest relevant "memory" files.
- AI Generation: To transform raw memory into coherent blog post drafts.
- User Interface: A project-centric dashboard for managing repos, files, and generated posts, with robust editing and publishing capabilities.
- Markdown Rendering: To beautifully display the generated content.
- Mobile-First Design: Ensuring a seamless experience across devices.
Under the Hood: A Deep Dive into the Implementation
Hitting "feature complete" means a lot of moving parts clicked into place. Here’s a detailed look at the major components we built or refined:
Data Layer: Structuring Our Content
- Prisma Schema Updates: We introduced
ProjectandBlogPostmodels toprisma/schema.prisma.Projectnow relates toUserandTenant, whileBlogPostnaturally links back toProject. A quickprisma db pushbrought our database up to speed.
Backend Services: The Engine Room
- Robust GitHub Connector: The existing stub in
src/server/services/github-connector.tswas replaced with a full-fledged BYOK (Bring Your Own Key) implementation. This gives users control over their GitHub tokens. Key functions include:resolveGitHubToken: Handling token authentication securely.fetchRepos: Listing available repositories.checkMemoryPath,listMemoryFiles,fetchMemoryFiles,fetchFileContent: Granular control over identifying and retrieving specific "memory" files within a repo.syncToDatabase: Persisting the imported file content.
- AI Blog Generator: We ported our
.github/scripts/blog_gen.pyprompt logic intosrc/server/services/blog-generator.ts. This TypeScript version utilizes our existingAnthropicProviderfor AI interaction and includes aparseFrontmatter()utility to extract metadata from generated markdown. - tRPC Routers for Project & Blog Management: The
src/server/trpc/routers/projects.tsfile became the central hub for all project-related operations. It now handles:- Full Project CRUD (Create, Read, Update, Delete).
- GitHub integration endpoints:
github.repos,check/files,import. - Comprehensive blog post management:
blogPosts.list,get,generate,generateBatch,update,delete, and a crucialunbloggedendpoint for identifying files ready for AI processing. - This
projectsRouterwas then registered insrc/server/trpc/router.ts.
Frontend UI: Bringing It All to Life
-
Markdown Rendering Excellence: We integrated
react-markdownwithremark-gfminsrc/components/markdown-renderer.tsx. To ensure beautiful, readable prose, we added@tailwindcss/typographytotailwind.config.tsand applied custom Nyx theme prose overrides. The result is stunning, readable content right out of the box. -
Navigation & User Experience:
src/components/layout/sidebar.tsx: Added a new "Projects" link (with aFolderGit2icon) between Workflows and Wardrobe.src/components/layout/mobile-nav.tsx: Replaced the old "Memory" (Brain icon) with "Projects" (FolderGit2) in the bottom navigation for mobile users, reflecting the new project-centric approach.
-
Core Project Pages:
src/app/(dashboard)/dashboard/projects/page.tsx: The main project list, featuring elegant cards with counts, badges, and a clear empty state.src/app/(dashboard)/dashboard/projects/new/page.tsx: A multi-step, scrollable form for creating new projects. This guides users through selecting a GitHub repo, choosing specific memory files, and toggling initial blog post generation.src/app/(dashboard)/dashboard/projects/[id]/page.tsx: The project detail page, designed with tabs for "Blog Posts," "Sources," and "Settings." It features collapsible blog cards, an inline markdown preview, and a convenient generate sheet.src/app/(dashboard)/dashboard/projects/[id]/blog/[postId]/page.tsx: The individual blog post viewer and editor. Here, users can see the full markdown, enter an edit mode, publish/unpublish, regenerate content, or delete posts. A sticky bottom action bar ensures key controls are always accessible.
-
New Dependencies: We pulled in
react-markdown,remark-gfm, and@tailwindcss/typographyto power our markdown rendering.
Navigating the Treacherous Waters: Lessons Learned
Not everything was smooth sailing. One particular TypeScript/tRPC interaction gave us a moment's pause.
- The Problem: We tried to use
ReturnType<typeof trpc.projects.blogPosts.unblogged.useQuery>as a prop type for ourGenerateSheetcomponent. The expectation was that TypeScript would correctly infer thedataproperty as an array ofUnbloggedEntryobjects. - The Failure: TypeScript, however, inferred
dataas{}. This meant accessingdata?.lengthordata?.map()resulted in type errors, asdatawas not recognized as potentially an array. - The Workaround & Lesson: Instead of relying on
ReturnTypefor this specific nested router query as a component prop, we defined an explicitUnbloggedEntryinterface and typed the prop as{ isLoading: boolean; data?: UnbloggedEntry[] }. This immediately resolved the type inference issue.
Takeaway: While ReturnType is incredibly useful for tRPC query results, be cautious when passing nested router query return types directly as component props. In some scenarios, especially with complex or deeply nested tRPC structures, TypeScript's inference might fall short, leading to any or {} types where more specific types are expected. Defining explicit interfaces for your component props can provide greater type safety and predictability.
What's Next? Solidifying the Foundation
With the core pipeline functional, our immediate focus shifts to testing and refinement:
- End-to-End Testing: The top priority is to thoroughly test the entire flow: create project → connect GitHub repo → import memory files → generate blog posts → view/edit/publish.
- Mobile Layout Verification: Confirm the mobile-first design holds up at 375px, paying close attention to sticky buttons, touch targets, and collapsible cards.
- Robust Error Handling: Implement comprehensive error handling for edge cases like expired GitHub tokens, empty memory files, and Anthropic API rate limits.
- UX Polish: Consider adding loading skeletons to the new project form's GitHub section during repo fetching to improve perceived performance.
- Minor Bug Fix: Address a pre-existing TypeScript error in
src/app/(dashboard)/dashboard/discussions/[id]/page.tsx:139where a Badge variant"outline"isn't allowed (a quick"default"fix). - Push to Remote: Once testing is complete,
git pushwill make this milestone available to the team.
This release is a huge leap forward, bringing us closer to a truly integrated development and content creation workflow. The journey continues, but for now, it's time to celebrate shipping!
{
"thingsDone": [
"Implemented Project and BlogPost Prisma models with relations.",
"Replaced GitHub connector stub with full BYOK (Bring Your Own Key) integration.",
"Created TypeScript port of AI blog generation script using Anthropic API.",
"Developed comprehensive tRPC router for project and blog post CRUD operations.",
"Integrated react-markdown and remark-gfm with Tailwind Typography for enhanced markdown rendering.",
"Updated sidebar and mobile navigation for project-centric UI.",
"Created dedicated UI pages for project listing, new project creation (multi-step form), project details (tabbed), and individual blog post viewing/editing.",
"Installed necessary frontend dependencies: react-markdown, remark-gfm, @tailwindcss/typography."
],
"pains": [
"tRPC ReturnType inference issue for nested router queries when passed as component props, leading to data being inferred as {} instead of an array."
],
"successes": [
"Achieved feature complete status for the full GitHub → Memory Import → Blog Generation pipeline.",
"Successfully implemented a mobile-first, project-based UI.",
"Integrated robust BYOK GitHub API connection.",
"Enabled AI-powered blog post generation from project memories.",
"Established a clean, custom-themed markdown rendering system.",
"Resolved complex type inference issues with explicit interfaces."
],
"techStack": [
"Next.js",
"TypeScript",
"tRPC",
"Prisma",
"React",
"Tailwind CSS",
"GitHub API",
"Anthropic API",
"react-markdown",
"remark-gfm",
"@tailwindcss/typography"
]
}