nyxcore-systems
8 min read

Unlocking Developer Wisdom: Building an AI-Powered Cross-Project Consolidation Engine

We just shipped a new feature that uses AI to extract patterns from my scattered developer notes across projects, turning raw memory into actionable insights and prompt hints. Here's how we built it, the challenges we faced, and the hard-won lessons learned.

AILLMPrismaNext.jsTypeScriptDeveloper ProductivityKnowledge ManagementAgentic Development

Every developer knows the feeling: you solve a tricky problem, discover a brilliant tool, or hit a frustrating roadblock. You make a mental note, maybe jot it down somewhere, and then move on. Weeks or months later, you encounter a similar situation, and that hard-won knowledge is scattered, forgotten, or buried deep in some README. What if you could aggregate all those experiences, not just as raw data, but as distilled, actionable patterns?

That was the driving force behind our latest feature: Cross-Project Consolidation. It's a meta-feature designed to turn individual developer "letters to myself" and project notes into a searchable, AI-powered knowledge base.

The Vision: Turning Scattered Thoughts into Strategic Patterns

The core idea was ambitious:

  1. Accumulate: Gather all my development notes, daily logs, and post-mortems from across multiple projects.
  2. Extract Patterns (via AI): Leverage large language models to identify recurring themes – successes, pains, solutions, architectural choices, and tools used.
  3. Organize & Search: Present these patterns in a searchable, categorized overview.
  4. Actionable Output: Make these insights exportable, particularly as "prompt hints" for future AI interactions or project planning.
  5. Integrate: Seamlessly link these consolidations back to individual project detail pages.

After a focused sprint, I'm thrilled to report: it's feature-complete, tested end-to-end, and pushed to origin/main! A user has already successfully created a consolidation from the UI, pulling insights from three distinct projects.

Under the Hood: The Architecture & Implementation

Building this required touching almost every layer of our stack.

Data Modeling with Prisma

The first step was extending our database schema. We introduced two new models:

  • Consolidation: Represents a single consolidation instance, linking to the user and tenant, and most importantly, storing an array of projectIds (String[] @db.Uuid) to define its scope.
  • ConsolidationPattern: Stores the AI-extracted patterns, linked to its parent Consolidation.

We also ensured proper cascade deletes and tenant/user relations for data integrity.

prisma
// Example snippet from prisma/schema.prisma
model Consolidation {
  id           String               @id @default(uuid())
  createdAt    DateTime             @default(now())
  updatedAt    DateTime             @updatedAt
  name         String
  description  String?
  status       ConsolidationStatus  @default(IDLE)
  projectIds   String[]             @db.Uuid // Projects included in this consolidation
  tenantId     String               @db.Uuid
  tenant       Tenant               @relation(fields: [tenantId], references: [id], onDelete: Cascade)
  userId       String               @db.Uuid
  user         User                 @relation(fields: [userId], references: [id], onDelete: Cascade)
  patterns     ConsolidationPattern[]
}

model ConsolidationPattern {
  id             String        @id @default(uuid())
  createdAt      DateTime      @default(now())
  updatedAt      DateTime      @updatedAt
  type           PatternType   // e.g., SUCCESS, PAIN, SOLUTION
  category       String?       // e.g., Frontend, Backend, DevOps
  description    String
  // ... other fields like tools, architecture, etc.
  consolidationId String        @db.Uuid
  consolidation  Consolidation @relation(fields: [consolidationId], references: [id], onDelete: Cascade)
}

The Backend Brain: Services and Routers

The heavy lifting for AI interaction and data processing happened in src/server/services/consolidation-service.ts. This service is responsible for:

  • extractPatterns(): The core AI call to Anthropic, taking raw content and returning structured JSON patterns.
  • generatePromptHints(): Formatting the extracted patterns into actionable Markdown, categorized for easy use.
  • budgetContents(): (More on this critical function later!) Responsible for intelligently truncating input to avoid LLM context overflow.

The src/server/trpc/routers/consolidation.ts exposes a comprehensive API:

  • Basic CRUD operations (list, get, create, delete).
  • AI generation endpoints (generate, regenerate).
  • Pattern search (patterns.search) with pagination and ILIKE for fuzzy matching.
  • Export functionality (export) for Markdown, JSON, or prompt hints.
  • Utility endpoints like availableProjects and byProject for UI convenience.

This router was then registered in src/server/trpc/router.ts.

The Frontend Experience: A Comprehensive UI

We aimed for a seamless user experience across the application:

  • Sidebar Navigation: A new Consolidation nav item with a Layers icon was added after Projects in src/components/layout/sidebar.tsx.
  • Consolidation List Page (/dashboard/consolidation): Displays all consolidations with status badges and pattern type count badges, giving an at-a-glance overview.
  • New Consolidation Form (/dashboard/consolidation/new): This is where the magic starts. It features a project multi-selector with checkboxes and content counts, a clear step machine (idle → creating → analyzing → done) to guide the user, sticky submit buttons, and even supports URL param pre-selection (?projectId=).
  • Consolidation Detail Page (/dashboard/consolidation/[id]): The heart of the feature. It's organized into three tabs:
    • Overview: A summary of the consolidation and distribution bars for pattern types.
    • Patterns: The core patterns view, with search, type filter chips, and expandable cards for detailed insights.
    • Export: Allows users to copy prompt hints to the clipboard or download the full consolidation as Markdown or JSON, with an inline preview.
  • Project Detail Integration (/dashboard/projects/[id]): A new "Analysis" tab was added, showing relevant consolidations for that specific project and a clear "Analyze Patterns" CTA to kick off new consolidations.

For the UI development, we experimented with spawning specialist agents (ui-list-page, ui-new-page, etc.). While the list page and project tab agents completed quickly and effectively, the more complex detail and new page agents struggled, requiring me to manually write those pages. This was a valuable lesson in itself!

The Gauntlet: Lessons Learned from the Trenches

No feature of this complexity ships without its share of battles. Here are the critical lessons we learned:

Battling the LLM Context Wall

The Problem: My initial naive approach was to send all memory entries and blog posts (10 letters + 9 blog posts) directly to the Anthropic API in one go. The result? A swift and brutal Anthropic API error: 400 input length and max_tokens exceed context limit: 198654 + 8192 > 200000. We hit the token limit hard.

The Solution: This led to the creation of budgetContents() within consolidation-service.ts. This function intelligently truncates individual entries to a maximum of 15,000 characters and caps the total input to the LLM at 500,000 characters. Crucially, we also realized that blog posts are often derived from the "letters" (developer notes), making them redundant and a wasteful double-count for token usage. By excluding them, we dropped the input from ~198k tokens to well within Anthropic's limits.

Takeaway: Always anticipate and actively manage LLM context limits. Develop robust input budgeting strategies that involve both truncation and intelligent data filtering to avoid redundant information. Don't just throw everything at the model; curate your input.

Navigating Prisma's JSON Type Nuances

The Problem: When trying to use Json? fields in Prisma with TypeScript, I initially reached for Record<string, unknown> for object types. This resulted in a TS2322: Type 'Record<string, unknown>' is not assignable to type 'NullableJsonNullValueInput | InputJsonValue' error.

The Solution: The fix was to explicitly cast objects to Prisma.InputJsonValue and use Prisma.JsonNull for null values. This required importing Prisma from @prisma/client.

typescript
// Before (failed)
const data = {
  jsonField: { key: 'value' } as Record<string, unknown> // TS error
};

// After (success)
import { Prisma } from '@prisma/client';

const data = {
  jsonField: { key: 'value' } as Prisma.InputJsonValue,
  nullableJsonField: null as Prisma.JsonNull // For explicit null
};

Takeaway: When working with Prisma's Json types in TypeScript, remember to use Prisma.InputJsonValue for objects and Prisma.JsonNull for explicit null values to satisfy the type checker. It's a common pattern that trips up many.

When AI Agents Hit Their Limits

The Problem: As mentioned earlier, I experimented with AI agents for generating complex UI pages. While agents for simpler, focused tasks (like a list page or adding a tab) were quick and effective, the agents assigned to the multi-tab detail page and the step-machine-driven new form page struggled. They took 10+ minutes and often remained "in_progress" without actually writing any files.

The Solution: For complex, multi-faceted UI components, I found it significantly faster and more reliable to write the pages manually.

Takeaway: AI agents are powerful tools, but they have their sweet spot. Use them for simple, well-defined, and focused tasks where the output structure is relatively straightforward. For complex, interdependent components, especially those with intricate state management or multiple interactive elements, manual development often remains the most efficient path. It's a valuable reminder that AI is a co-pilot, not yet an autonomous developer for intricate features.

The Road Ahead

With the core consolidation feature complete, our immediate next steps involve thorough testing and refinement:

  • Verifying the full consolidation flow: create → generate → view patterns → export prompt hints.
  • Testing the regenerate flow from the detail page.
  • Ensuring mobile layout responsiveness for filter chips, expandable cards, and export buttons.
  • Confirming "Copy as Prompt Hints" clipboard functionality.
  • Testing the pre-selection of projects when initiating consolidation from a project's Analysis tab.
  • Considering future enhancements like a dedicated "consolidation" workflow type and a global pattern search across all consolidations.

This feature represents a significant step towards turning our collective development experience into a structured, actionable knowledge base. It's exciting to think about how these AI-extracted patterns will empower us to build better, learn faster, and avoid repeating past mistakes.


json
{"thingsDone":["Built cross-project consolidation feature","Implemented AI pattern extraction and prompt hint generation","Designed and implemented Prisma models for Consolidation and ConsolidationPattern","Developed comprehensive backend API with tRPC","Created full frontend UI: list, new, detail, and project integration pages","Addressed LLM context limits with content budgeting","Resolved Prisma JSON type casting issues","Identified optimal use cases for AI agents in development"],"pains":["Hitting Anthropic API context limits","Prisma/TypeScript JSON type mismatches","AI agents failing on complex UI pages"],"successes":["Successful end-to-end feature completion and testing","Effective solution for LLM context management","Clean integration of AI services","Robust UI for complex data and workflows"],"techStack":["Next.js","React","TypeScript","Prisma","PostgreSQL","tRPC","Anthropic API (LLM)","Tailwind CSS"]}