This document provides specialized workflows for different types of tasks in the leaderboard system. Each agent represents a specific role with its own workflow, considerations, and best practices.
Purpose: Create and test new data source plugins for the leaderboard system.
-
Scaffold Plugin Project
pnpm create-leaderboard-plugin <path> # Example: pnpm create-leaderboard-plugin ../leaderboard-github-plugin
The CLI will prompt for:
- Plugin name (e.g., 'github', 'slack', 'jira')
- Plugin description
- Author name
This generates a complete project structure with:
package.jsonwith correct dependenciestsconfig.jsonwith proper configurationvitest.config.tsfor testingsrc/index.tswith plugin templatesrc/__tests__/plugin.test.tswith test examplesREADME.mdwith documentation
-
Implement Setup Method
Define activity types in the
setup()method:import { activityDefinitionQueries } from "@ohcnetwork/leaderboard-api"; async setup(ctx: PluginContext): Promise<void> { // Define all activity types your plugin will track await activityDefinitionQueries.upsert(ctx.db, { slug: "pr_opened", name: "Pull Request Opened", description: "Opened a pull request", points: 5, meta: { icon: "git-pull-request" }, }); await activityDefinitionQueries.upsert(ctx.db, { slug: "pr_merged", name: "Pull Request Merged", description: "Had a pull request merged", points: 10, meta: { icon: "git-merge" }, }); }
-
Implement Scrape Method
Fetch and store activities:
import { activityQueries, contributorQueries } from "@ohcnetwork/leaderboard-api"; async scrape(ctx: PluginContext): Promise<void> { const { apiToken, organization } = ctx.config; // Fetch data from external API const prs = await fetchPullRequests(apiToken, organization); for (const pr of prs) { // Ensure contributor exists await contributorQueries.upsert(ctx.db, { username: pr.author.login, name: pr.author.name, role: "contributor", // or determine from your logic avatar_url: pr.author.avatar_url, }); // Store activity await activityQueries.create(ctx.db, { slug: `pr-${pr.id}`, contributor: pr.author.login, activity_definition: pr.merged ? "pr_merged" : "pr_opened", title: pr.title, occurred_at: pr.created_at, link: pr.html_url, points: pr.merged ? 10 : 5, meta: { repository: pr.repository, additions: pr.additions, deletions: pr.deletions, }, }); } ctx.logger.info(`Processed ${prs.length} pull requests`); }
-
Implement Aggregate Method (Optional)
Compute plugin-specific aggregates after the main leaderboard aggregation:
import { activityQueries, contributorQueries, contributorAggregateQueries } from "@ohcnetwork/leaderboard-api"; async aggregate(ctx: PluginContext): Promise<void> { const contributors = await contributorQueries.getAll(ctx.db); for (const contributor of contributors) { const activities = await activityQueries.getByContributor(ctx.db, contributor.username); const mergedPRs = activities.filter(a => a.activity_definition === "pr_merged"); await contributorAggregateQueries.upsert(ctx.db, { aggregate: "pr_merged_count", contributor: contributor.username, value: { type: "number", value: mergedPRs.length, format: "integer" }, meta: { calculated_at: new Date().toISOString() }, }); } ctx.logger.info("PR merge count aggregates computed"); }
The
aggregate()method runs after the main leaderboard aggregation, so standard aggregates liketotal_activity_pointsandactivity_countare already available. -
Use Query Builders
Always use provided query builders from the API package:
contributorQueries: create, upsert, getByUsername, getAll, etc.activityQueries: create, getByContributor, getByDateRange, etc.activityDefinitionQueries: upsert, getBySlug, getAll, etc.
-
Write Tests
Create comprehensive tests in
src/__tests__/plugin.test.ts:import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { createDatabase, initializeSchema, } from "@ohcnetwork/leaderboard-api"; import plugin from "../index"; describe("My Plugin", () => { let db: Database; beforeEach(async () => { db = createDatabase(":memory:"); await initializeSchema(db); }); afterEach(async () => { await db.close(); }); it("should define activity types in setup", async () => { const ctx = { db, config: {}, orgConfig: { /* mock org config */ }, logger: createLogger(false), }; await plugin.setup(ctx); const definitions = await activityDefinitionQueries.getAll(db); expect(definitions.length).toBeGreaterThan(0); }); // More tests... });
-
Export as ES Module
Ensure your plugin exports correctly:
import type { Plugin } from "@ohcnetwork/leaderboard-api"; export default { name: "my-plugin", version: "1.0.0", setup, scrape, } satisfies Plugin;
-
Test with Plugin Runner
Build and test your plugin:
pnpm build pnpm test # Test with actual plugin runner cd /path/to/leaderboard-monorepo # Update config.yaml to point to your plugin pnpm build:data
-
Deploy and Configure
- Build your plugin:
pnpm build - Deploy
dist/index.jsto a accessible URL (GitHub raw, CDN, etc.) - Configure in data repository's
config.yaml:leaderboard: plugins: my-plugin: name: My Plugin source: https://example.com/plugins/my-plugin/manifest.js config: apiToken: ${{ env.MY_API_TOKEN }} organization: my-org
- Build your plugin:
docs/plugins/creating-plugins.mdx- Complete plugin development guidepackages/plugin-dummy/src/index.ts- Reference implementationpackages/api/src/types.ts- Plugin interface and typespackages/api/src/queries.ts- Available query builders
- Keep plugins focused on one data source
- Use bulk operations for better performance
- Log progress for debugging
- Handle API rate limits gracefully
- Cache responses when appropriate
- Use environment variables for secrets
- Test with realistic data volumes
Purpose: Set up and manage data repositories for organizations using the leaderboard system.
-
Initialize Data Repository
pnpm create-data-repo <path> # Example: pnpm create-data-repo ../my-org-leaderboard-data
Interactive prompts will collect:
- Organization name, description, URL, logo
- Social media links (GitHub, Slack, LinkedIn, YouTube, email)
- SEO metadata (title, description, images, site URL)
- Data source repository URL
- Optional custom theme CSS URL
- Roles (with option for defaults: core, contributor)
-
Review Generated Structure
my-org-leaderboard-data/ ├── config.yaml # Organization configuration ├── README.md # Repository documentation ├── .gitignore # Git ignore rules ├── contributors/ # Contributor profiles (empty) └── activities/ # Activity records (empty) -
Configure Plugins
Edit
config.yamlto uncomment and configure plugins:leaderboard: plugins: github: name: GitHub Plugin source: https://raw.githubusercontent.com/org/plugin/main/manifest.js config: githubToken: ${{ env.GITHUB_TOKEN }} githubOrg: my-org repositories: - repo1 - repo2 slack: name: Slack Plugin source: https://raw.githubusercontent.com/org/plugin/main/manifest.js config: slackApiToken: ${{ env.SLACK_API_TOKEN }} slackChannel: ${{ env.SLACK_CHANNEL }}
-
Set Environment Variables
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxx export SLACK_API_TOKEN=xoxb-xxxxxxxxxxxxx export LEADERBOARD_DATA_DIR=/path/to/my-org-leaderboard-data
-
Add Contributor Profiles (Optional)
Manually create contributor Markdown files if needed:
cd contributors cat > alice.md << 'EOF' --- username: alice name: Alice Smith role: core title: Senior Engineer avatar_url: https://github.com/alice.png social_profiles: github: https://github.com/alice linkedin: https://linkedin.com/in/alice-smith joining_date: 2020-01-15 --- Alice is a senior engineer specializing in backend systems. EOF
-
Run Data Collection
cd /path/to/leaderboard-monorepo pnpm build:dataThis will:
- Import existing contributors and activities
- Run plugin setup methods
- Scrape new activities from configured sources
- Compute aggregates
- Evaluate badge rules
- Export updated data back to files
-
Commit and Push
cd /path/to/my-org-leaderboard-data git add contributors/ activities/ aggregates/ badges/ git commit -m "Update leaderboard data" git push
-
Configure Aggregates (Optional)
Create
aggregates/definitions.jsonfor custom metrics:{ "definitions": [ { "slug": "custom_metric", "name": "Custom Metric", "description": "A custom metric", "type": "contributor", "query": "SELECT COUNT(*) FROM activity WHERE contributor = :username" } ] } -
Configure Badges (Optional)
Create
badges/definitions.jsonfor achievements:{ "badges": [ { "slug": "early_bird", "name": "Early Bird", "description": "Joined in the first year", "icon": "star", "rule": { "type": "count", "min": 1, "filter": { "joining_date_before": "2021-01-01" } } } ] }
docs/getting-started/index.mdx- Setup guidedocs/data-management.mdx- Data management patternsdocs/getting-started/configuration.mdx- Config referenceapps/leaderboard-web/config.example.yaml- Example configuration
- Use environment variables for all secrets
- Keep data repository separate from code repository
- Commit contributor and activity files to git
- Run scraping on a schedule (cron job, GitHub Actions)
- Review generated data before committing
- Use custom themes for branding
- Document your organization's specific setup
Purpose: Optimize database queries and aggregations for performance.
-
Identify Slow Queries
Enable debug logging to see query execution:
DEBUG=true pnpm build:data
Look for log messages showing query duration.
-
Use Query Builders First
Start with provided query builders from
packages/api/src/queries.ts:import { contributorQueries, activityQueries, } from "@ohcnetwork/leaderboard-api"; // Optimized queries with proper indexes const activities = await activityQueries.getByContributor(db, "alice"); const recentActivities = await activityQueries.getByDateRange( db, new Date("2024-01-01"), new Date("2024-12-31"), );
-
Write Custom SQL for Complex Queries
For complex aggregations not covered by query builders:
const result = await db.execute( ` SELECT c.username, c.name, COUNT(a.id) as activity_count, SUM(ad.points) as total_points, MAX(a.occurred_at) as last_activity FROM contributor c LEFT JOIN activity a ON c.username = a.contributor LEFT JOIN activity_definition ad ON a.activity_definition = ad.slug WHERE c.role = ? AND a.occurred_at >= ? GROUP BY c.username, c.name ORDER BY total_points DESC LIMIT 100 `, ["core", "2024-01-01"], );
-
Add Indexes for Performance
If modifying schema, add indexes for frequently queried columns:
CREATE INDEX IF NOT EXISTS idx_activity_contributor ON activity(contributor); CREATE INDEX IF NOT EXISTS idx_activity_occurred_at ON activity(occurred_at); CREATE INDEX IF NOT EXISTS idx_activity_definition ON activity(activity_definition);
-
Test with Realistic Data
Generate large datasets for testing:
pnpm setup:dev --contributors 100 --days 365
Measure query performance:
const start = Date.now(); const result = await expensiveQuery(db); console.log(`Query took ${Date.now() - start}ms`);
-
Add Tests
Add performance tests in
packages/api/src/__tests__/queries.test.ts:it("should query large datasets efficiently", async () => { // Insert 10k activities for (let i = 0; i < 10000; i++) { await activityQueries.create(db, { /* ... */ }); } const start = Date.now(); const result = await activityQueries.getByContributor(db, "alice"); const duration = Date.now() - start; expect(duration).toBeLessThan(100); // Should complete in <100ms });
- Database: SQLite (LibSQL) - optimized for reads
- Workload: Read-heavy at build time, not runtime
- Optimize for: Static site generation speed
- Indexes: Critical for large datasets
- Batch Operations: Use
db.batch()for bulk inserts - Memory: Consider memory usage with large result sets
packages/api/src/queries.ts- Query builderspackages/api/src/schema.ts- Database schemapackages/api/src/__tests__/queries.test.ts- Query tests
- Use
EXPLAIN QUERY PLANto understand query execution - Limit result sets with proper WHERE clauses
- Use pagination for large lists
- Cache computed results when appropriate
- Profile with realistic data volumes
- Consider denormalization for complex aggregations
Purpose: Create and modify badge rules and achievements.
-
Define Badge in Definitions
Create or edit
badges/definitions.jsonin your data repository:{ "badges": [ { "slug": "prolific_contributor", "name": "Prolific Contributor", "description": "Completed 100+ activities", "icon": "trophy", "rule": { "type": "count", "min": 100 } }, { "slug": "point_master", "name": "Point Master", "description": "Earned 1000+ points", "icon": "star", "rule": { "type": "total_points", "min": 1000 } }, { "slug": "streak_champion", "name": "Streak Champion", "description": "30-day activity streak", "icon": "flame", "rule": { "type": "streak", "min": 30 } } ] } -
Understand Rule Types
Count Rule: Number of activities matching filters
{ "slug": "pr_expert", "name": "PR Expert", "rule": { "type": "count", "min": 50, "filter": { "activity_definitions": ["pr_merged", "pr_opened"], "role": "core", "date_from": "2024-01-01", "date_to": "2024-12-31" } } }Total Points Rule: Sum of points from matching activities
{ "slug": "top_scorer", "name": "Top Scorer", "rule": { "type": "total_points", "min": 500, "filter": { "date_from": "2024-01-01" } } }Streak Rule: Consecutive days with activity
{ "slug": "daily_contributor", "name": "Daily Contributor", "rule": { "type": "streak", "min": 7, "filter": { "activity_definitions": ["*"], // All activities "date_from": "2024-01-01" } } }Per-Activity-Definition Streak: Streak for specific activity types
{ "slug": "pr_streak", "name": "PR Streak", "rule": { "type": "streak", "min": 5, "filter": { "activity_definitions": ["pr_merged"] // Only merged PRs } } }Multiple Activity Definition Streak: Combined streak
{ "slug": "code_review_streak", "name": "Code Review Streak", "rule": { "type": "streak", "min": 10, "filter": { "activity_definitions": ["pr_reviewed", "pr_commented"] } } }Regex Pattern Matching: Match activity definitions with regex
{ "slug": "github_all_star", "name": "GitHub All Star", "rule": { "type": "count", "min": 100, "filter": { "activity_definitions": ["^github_.*"] // All GitHub activities } } } -
Configure Filters
Available filters:
activity_definitions: Array of activity slugs or regex patterns (["*"]for all)role: Filter by contributor roledate_from: ISO date string (YYYY-MM-DD)date_to: ISO date string (YYYY-MM-DD)
-
Test Badge Evaluation
Run badge evaluation:
pnpm build:data # Includes badge evaluationCheck results in
badges/contributors/{username}.json:{ "username": "alice", "badges": [ { "slug": "prolific_contributor", "earned_at": "2024-03-15T10:30:00Z", "value": 150 } ] } -
Debug Badge Rules
Enable debug logging:
DEBUG=true pnpm build:data
Review logs for badge evaluation details.
| Type | Description | Value Meaning |
|---|---|---|
count |
Number of matching activities | Activity count |
total_points |
Sum of points from activities | Total points |
streak |
Consecutive days with activity | Streak length (days) |
docs/badges.mdx- Badge system documentationpackages/plugin-runner/src/rules/evaluator.ts- Badge evaluation logicpackages/plugin-runner/src/rules/__tests__/evaluator.test.ts- Badge tests
- Start with simple rules, add complexity as needed
- Test with various contributor profiles
- Use date filters for time-bound badges
- Combine multiple activity definitions for versatile badges
- Use regex patterns for flexible matching
- Document badge meanings clearly
- Consider badge hierarchy (bronze/silver/gold variants)
Purpose: Modify and enhance the Next.js frontend application.
-
Understand Constraints
- Static Export Only: No server-side runtime features
- SSG at Build Time: All data loaded during
next build - No API Routes: Cannot use Next.js API routes
- Unoptimized Images: Image optimization disabled
- Data from Database: All data from LibSQL at build time
-
Page Structure
All pages in
apps/leaderboard-web/app/:// app/leaderboard/page.tsx import { getAllContributors } from "@/lib/data/loader"; export default async function LeaderboardPage() { // Data loaded at build time (SSG) const contributors = await getAllContributors(); return ( <div> {contributors.map(c => ( <ContributorCard key={c.username} contributor={c} /> ))} </div> ); }
-
Data Loading
Use data loaders from
apps/leaderboard-web/lib/data/loader.ts:import { getAllContributors, getContributorByUsername, getActivitiesByContributor, getActivityDefinitions, getAggregates, } from "@/lib/data/loader";
-
Components
Use shadcn/ui components from
apps/leaderboard-web/components/ui/:import { Button } from "@/components/ui/button"; import { Card, CardHeader, CardTitle, CardContent, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge";
-
Styling
Use Tailwind CSS classes:
<div className="container mx-auto px-4 py-8"> <h1 className="text-4xl font-bold mb-6">Leaderboard</h1> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {/* Content */} </div> </div>
-
Theme Customization
Users can override styles via
data/theme.css::root { --primary: 220 90% 56%; --primary-foreground: 0 0% 100%; /* ... other CSS variables */ }
-
Dynamic Routes
Generate static paths at build time:
// app/[username]/page.tsx import { getAllContributors, getContributorByUsername } from "@/lib/data/loader"; export async function generateStaticParams() { const contributors = await getAllContributors(); return contributors.map(c => ({ username: c.username, })); } export default async function ContributorPage({ params, }: { params: { username: string }; }) { const contributor = await getContributorByUsername(params.username); return <div>{/* Profile content */}</div>; }
-
Documentation Pages
Documentation uses Fumadocs, defined in MDX files in
docs/.
- ✅ Server Components (for SSG)
- ✅ Client Components (with 'use client')
- ✅ Static generation (generateStaticParams)
- ✅ Tailwind CSS
- ❌ Server-side runtime (no SSR)
- ❌ API routes
- ❌ Image optimization
- ❌ Dynamic data at runtime
apps/leaderboard-web/app/- Pages and layoutsapps/leaderboard-web/components/- React componentsapps/leaderboard-web/lib/data/loader.ts- Data loadingapps/leaderboard-web/lib/config/- Configurationapps/leaderboard-web/next.config.ts- Next.js config
- All data must be available at build time
- Use client components sparingly (only for interactivity)
- Optimize bundle size (tree shaking, code splitting)
- Test static export:
pnpm build && pnpm start - Preview locally before deploying
- Use semantic HTML for accessibility
- Test responsive design at various breakpoints
Purpose: Write comprehensive tests for the leaderboard system.
- Framework: Vitest with TypeScript
- Location:
src/__tests__/directories - Naming:
{module-name}.test.ts - Coverage: Happy path and edge cases
- Isolation: Use in-memory database
- Cleanup: Always clean up resources
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { createDatabase, initializeSchema } from "@ohcnetwork/leaderboard-api";
import type { Database } from "@ohcnetwork/leaderboard-api";
import { createLogger } from "../logger";
describe("Feature Name", () => {
let db: Database;
const logger = createLogger(false); // Disable logging in tests
beforeEach(async () => {
// Setup: Create fresh database
db = createDatabase(":memory:");
await initializeSchema(db);
// Additional setup (seed data, etc.)
});
afterEach(async () => {
// Cleanup: Close database
await db.close();
// Remove test files if created
// await rm(testDir, { recursive: true, force: true });
});
it("should handle happy path", async () => {
// Arrange: Set up test data
const input = { username: "alice", name: "Alice" };
// Act: Execute the function
const result = await functionUnderTest(db, input);
// Assert: Verify results
expect(result).toBeDefined();
expect(result.username).toBe("alice");
});
it("should handle empty input", async () => {
const result = await functionUnderTest(db, []);
expect(result).toHaveLength(0);
});
it("should handle errors gracefully", async () => {
await expect(functionUnderTest(db, null)).rejects.toThrow("Invalid input");
});
it("should validate constraints", async () => {
// Test database constraints, validations, etc.
});
});Unit Tests: Test individual functions in isolation
describe("contributorQueries.getByUsername", () => {
it("should return contributor by username", async () => {
await contributorQueries.create(db, { username: "alice" /* ... */ });
const result = await contributorQueries.getByUsername(db, "alice");
expect(result).not.toBeNull();
});
it("should return null for non-existent username", async () => {
const result = await contributorQueries.getByUsername(db, "nonexistent");
expect(result).toBeNull();
});
});Integration Tests: Test multiple components together
describe("Plugin Integration", () => {
it("should import, scrape, and export data", async () => {
// Import existing data
await importContributors(db, dataDir, logger);
// Run plugin
await plugin.setup(ctx);
await plugin.scrape(ctx);
// Export data
await exportActivities(db, dataDir, logger);
// Verify exported files
const exported = await readdir(join(dataDir, "activities"));
expect(exported.length).toBeGreaterThan(0);
});
});Edge Cases: Test boundary conditions
describe("Edge Cases", () => {
it("should handle very long usernames", async () => {
const longUsername = "a".repeat(255);
await contributorQueries.create(db, { username: longUsername });
const result = await contributorQueries.getByUsername(db, longUsername);
expect(result?.username).toBe(longUsername);
});
it("should handle special characters in slugs", async () => {
// Test with special characters
});
it("should handle large datasets efficiently", async () => {
// Insert 10k records and verify performance
});
});import { vi } from "vitest";
describe("Plugin with API calls", () => {
it("should fetch data from API", async () => {
// Mock fetch
const mockFetch = vi.fn().mockResolvedValue({
json: () => Promise.resolve([{ id: 1, title: "Test" }]),
});
global.fetch = mockFetch;
await plugin.scrape(ctx);
expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining("api.example.com"),
);
});
});import { mkdir, writeFile, rm } from "fs/promises";
import { join } from "path";
describe("File Operations", () => {
const testDir = "./test-data-temp";
beforeEach(async () => {
await mkdir(testDir, { recursive: true });
});
afterEach(async () => {
await rm(testDir, { recursive: true, force: true });
});
it("should read and parse markdown files", async () => {
await writeFile(
join(testDir, "test.md"),
"---\nusername: alice\n---\nContent",
"utf-8",
);
const result = await importFromMarkdown(testDir);
expect(result).toHaveLength(1);
});
});# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run specific test file
pnpm test path/to/test.test.ts
# Generate coverage report
pnpm test:coverage- Test one thing per test case
- Use descriptive test names
- Arrange-Act-Assert pattern
- Mock external dependencies
- Test error conditions
- Clean up all resources
- Use in-memory database for speed
- Verify database state after operations
Purpose: Create and maintain documentation for the leaderboard system.
-
System Documentation:
docs/directory- Written in MDX (Markdown + JSX)
- Powered by Fumadocs
- Deployed with the web app
-
Package Documentation: README files in each package
-
API Documentation: JSDoc comments in source code
- Inline documentation for functions, types, interfaces
- Picked up by TypeScript language server
---
title: Feature Name
description: Brief description of the feature
---
# Feature Name
Brief introduction paragraph explaining what this feature does and why it's useful.
## Overview
High-level explanation of the feature.
## Quick Start
\`\`\`bash
# Quick example to get started
pnpm install
pnpm build
\`\`\`
## Detailed Guide
### Step 1: Setup
Detailed instructions...
### Step 2: Configuration
More details...
## Examples
### Example 1: Basic Usage
\`\`\`typescript
import { feature } from "@package/name";
const result = await feature.doSomething();
\`\`\`
### Example 2: Advanced Usage
\`\`\`typescript
// More complex example
\`\`\`
## API Reference
### `functionName(param: Type): ReturnType`
Description of what the function does.
**Parameters:**
- `param` - Description of parameter
**Returns:**
- Description of return value
**Example:**
\`\`\`typescript
const result = functionName("value");
\`\`\`
## Architecture
\`\`\`mermaid
graph LR
A[Input] --> B[Process]
B --> C[Output]
\`\`\`
## Best Practices
- Do this
- Don't do that
- Consider this
## Troubleshooting
### Problem: Error message
**Solution:** How to fix it.
## Related
- [Related Doc 1](/docs/related)
- [Related Doc 2](/docs/other)Use Mermaid for visualizing architecture and flows:
graph TB
DataSource[Data Source] --> Plugin[Plugin]
Plugin --> Database[(Database)]
Database --> NextJS[Next.js]
NextJS --> StaticSite[Static Site]
sequenceDiagram
participant User
participant CLI
participant Plugin
participant Database
User->>CLI: Run scrape command
CLI->>Plugin: Load plugin
Plugin->>Database: Setup activity definitions
Plugin->>Database: Scrape and store activities
CLI->>Database: Export to files
/**
* Fetch all contributors from the database
*
* @param db - Database instance
* @param filter - Optional filter criteria
* @returns Array of contributors
*
* @example
* ```typescript
* const contributors = await getAllContributors(db);
* const coreMembers = await getAllContributors(db, { role: "core" });
* ```
*/
export async function getAllContributors(
db: Database,
filter?: ContributorFilter,
): Promise<Contributor[]> {
// Implementation
}- Clear title and description
- Quick start example
- Step-by-step instructions
- Code examples that work
- Architecture diagrams where helpful
- API reference for public functions
- Links to related documentation
- Troubleshooting section
- Examples are up-to-date with codebase
- No broken links
- Proper formatting and structure
- Write for different audiences (beginners, advanced users)
- Include working code examples
- Use diagrams to explain complex concepts
- Keep examples up-to-date
- Link to relevant source files
- Test code examples before publishing
- Use consistent terminology
- Include troubleshooting for common issues
- Read First: Review existing code and documentation before starting
- Follow Conventions: Adhere to established patterns in the codebase
- Test Thoroughly: Write and run tests for all changes
- Document Changes: Update documentation when making significant changes
- Use Type Safety: Leverage TypeScript's type system
- Handle Errors: Always handle errors gracefully with proper logging
- Ask Questions: When in doubt, ask for clarification
- Review Changes: Double-check your work before considering it complete
- Keep It Simple: Prefer simple solutions over complex ones
- Be Consistent: Match the style and patterns of surrounding code