Dynamic Content Loading Implementation Plan
Overview
Transform hardcoded guides and personas in Sasha Studio to load dynamically from markdown files, combining both public user content and private system content with appropriate badging.
Design Philosophy
- Simple: Fetch fresh content when panels open (no WebSockets)
- Reliable: Direct file reading with fallbacks
- Dual Sources: Combine public and private content with "System" badges
Architecture
1. Directory Structure
docs/
βββ personas/ # User-created personas
β βββ marketing-sasha.md
β βββ sales-sasha.md
βββ guides/ # User-created guides
β βββ custom-workflows/
β βββ my-process.md
βββ private/ # System content (badged)
βββ personas/
β βββ general-sasha.md
β βββ analyst-sasha.md
β βββ technical-sasha.md
β βββ compliance-sasha.md
β βββ creative-sasha.md
βββ guides/
βββ research-analysis/
β βββ competitor-analysis.md
β βββ market-research.md
βββ documentation/
β βββ technical-docs.md
β βββ user-guide.md
βββ business-strategy/
βββ swot-analysis.md
βββ business-canvas.md
2. Content Format
Persona Markdown Structure
---
name: General Sasha
icon: Sparkles
description: Your AI assistant with deep knowledge
traits:
- Comprehensive knowledge
- Balanced approach
- Adaptive communication
order: 1
---
# General Sasha
Detailed persona description and capabilities...
Guide Markdown Structure
---
title: Competitor Analysis Framework
description: Comprehensive methodology for analyzing competitors
category: RESEARCH & ANALYSIS
icon: MagnifyingGlass
order: 1
---
# Competitor Analysis Framework
Guide content with markdown formatting...
3. Implementation Components
Backend Service (content-reader.js)
- Reads markdown files from both docs/ and docs/private/ directories
- Merges content with source tracking (system vs user)
- Parses frontmatter using gray-matter
- Returns JSON with source metadata
- System content takes priority in ordering
API Endpoints
GET /api/personas- Returns all personas (system + user)GET /api/guides- Returns categorized guides (system + user)- Each item includes
source: 'system' | 'user'field - No caching - fresh read on each request
Frontend Updates
- PersonasPanel fetches on open
- GuidesPanel fetches on open
- Shows "System" badge for private content
- Loading states during fetch
- Fallback to hardcoded if API fails
Implementation Steps
Phase 1: Core Implementation
- Install gray-matter dependency
- Create directory structure
- Convert existing content to markdown
- Create content-reader service
- Add API endpoints
- Update frontend panels
- Test functionality
Phase 2: Future Enhancements (V2)
- Support for npm package content (@sasha/core-content)
- Organization-specific content layers
- Content versioning and updates
- Remote content loading from CDN
Benefits
- Content Management: Non-developers can add/edit content
- No Restart Required: Changes visible on panel reopen
- Version Control: Track content changes in Git
- Scalability: Ready for future content sources
- Simplicity: No complex syncing or caching
Technical Details
Dependencies
- gray-matter: ^4.0.3 (frontmatter parsing)
File Naming Convention
- Personas:
{id}-sasha.md(e.g., general-sasha.md) - Guides:
{guide-name}.md(e.g., competitor-analysis.md)
Content Reader Implementation Example
// server/services/content-reader.js
export async function readPersonas() {
const personas = [];
// Read system personas from private directory
const privateDir = path.join(process.cwd(), 'docs/private/personas');
if (await fs.access(privateDir).then(() => true).catch(() => false)) {
const systemPersonas = await readPersonasFromDir(privateDir);
systemPersonas.forEach(p => {
p.source = 'system';
p.order = p.order || 0; // System personas come first
personas.push(p);
});
}
// Read user personas from public directory
const publicDir = path.join(process.cwd(), 'docs/personas');
if (await fs.access(publicDir).then(() => true).catch(() => false)) {
const userPersonas = await readPersonasFromDir(publicDir);
userPersonas.forEach(p => {
p.source = 'user';
p.order = (p.order || 999) + 100; // User personas after system
personas.push(p);
});
}
return personas.sort((a, b) => a.order - b.order);
}
Category Mapping
Folder names map to display categories:
research-analysis/β "RESEARCH & ANALYSIS"documentation/β "DOCUMENTATION"business-strategy/β "BUSINESS STRATEGY"
Frontend Badge Implementation
// PersonasPanel.jsx - System badge display
const PersonaCard = ({ persona }) => (
<div className="persona-card">
<div className="flex items-center gap-2">
<h3>{persona.name}</h3>
{persona.source === 'system' && (
<span className="px-2 py-0.5 text-xs rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300">
System
</span>
)}
</div>
{/* Rest of card content */}
</div>
);
Error Handling
- Missing directories: Create on first run
- Missing files: Fall back to hardcoded defaults
- Parse errors: Log and skip problematic files
- Network errors: Use cached/default content
Migration Path
Current State
- Hardcoded arrays in components
- No external content management
After Implementation
- Markdown files as source of truth
- Dynamic loading on demand
- Extensible for future sources
Future Vision
- Core content from npm packages
- Organization content repositories
- User-generated content
- Content marketplace
Testing Checklist
- Add new persona file β appears in panel
- Edit existing guide β changes reflect
- Delete file β falls back gracefully
- Invalid markdown β doesn't break app
- Multiple categories β organize correctly
- Performance β loads quickly
Rollback Plan
If issues arise, components can revert to hardcoded content by:
- Commenting out fetch logic
- Using fallback arrays
- No database or complex state to revert
Success Metrics
- Content loads in < 200ms
- Zero code changes for content updates
- Non-technical users can manage content
- System remains stable with invalid content
Created: 2025-01-09
Status: Ready for Implementation
Author: Sasha Studio Team