Some_claude_skills recovery-coach-patterns
Follow Recovery Coach codebase patterns and conventions. Use when writing new code, components, API routes, or database queries. Activates for general development, code organization, styling,
install
source · Clone the upstream repo
git clone https://github.com/curiositech/some_claude_skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/curiositech/some_claude_skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/recovery-coach-patterns" ~/.claude/skills/curiositech-some-claude-skills-recovery-coach-patterns && rm -rf "$T"
manifest:
.claude/skills/recovery-coach-patterns/SKILL.mdsource content
Recovery Coach Development Patterns
This skill helps you follow the established patterns and conventions in the Recovery Coach codebase.
When to Use
✅ USE this skill for:
- Writing new components, pages, or API routes in Recovery Coach
- Following established code organization patterns
- Implementing database queries with Drizzle ORM
- Understanding project architecture and conventions
- Styling components to match the design system
❌ DO NOT use for:
- Crisis intervention implementation → use
crisis-response-protocol - General Next.js questions → use Next.js docs
- AI/LLM integration patterns → use
modern-drug-rehab-computer - Content moderation → use
recovery-community-moderator
Project Structure
src/ ├── app/ # Next.js App Router │ ├── api/ # API routes (REST endpoints) │ │ ├── auth/ # Authentication endpoints │ │ ├── check-in/ # Daily check-in endpoints │ │ ├── chat/ # AI coaching endpoints │ │ └── admin/ # Admin-only endpoints │ ├── admin/ # Admin dashboard page │ ├── settings/ # User settings page │ └── page.tsx # Home page ├── components/ # React components │ ├── ui/ # Base UI components (Button, Card, etc.) │ └── *.tsx # Feature components ├── lib/ # Core business logic │ ├── ai/ # Anthropic integration │ ├── hipaa/ # HIPAA compliance utilities │ ├── auth.ts # Authentication │ ├── db.ts # Database connection │ └── rate-limit.ts # Rate limiting ├── db/ # Database schema (Drizzle ORM) │ ├── schema.ts # Table definitions │ └── secure-db.ts # RLS-enforced queries └── test/ # Test utilities features/ # Feature manifests (YAML) docs/ # Documentation scripts/ # Build and utility scripts
API Route Pattern
// src/app/api/[feature]/route.ts import { NextResponse } from 'next/server'; import { getSession } from '@/lib/auth'; import { createRateLimiter } from '@/lib/rate-limit'; import { logPHIAccess } from '@/lib/hipaa/audit'; import { z } from 'zod'; // 1. Define rate limiter const rateLimiter = createRateLimiter({ windowMs: 60000, maxRequests: 30, keyPrefix: 'api:feature' }); // 2. Define input schema const RequestSchema = z.object({ field: z.string().min(1).max(1000), }); export async function POST(request: Request) { // 3. Check authentication const session = await getSession(); if (!session) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // 4. Apply rate limiting const rateLimitResult = await rateLimiter.check(session.userId); if (!rateLimitResult.allowed) { return NextResponse.json( { error: 'Rate limit exceeded' }, { status: 429, headers: rateLimitResult.headers } ); } // 5. Parse and validate input const body = await request.json(); const parsed = RequestSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: 'Invalid input', details: parsed.error.issues }, { status: 400 } ); } // 6. Perform operation const result = await performOperation(session.userId, parsed.data); // 7. Audit log (if PHI) await logPHIAccess(session.userId, 'feature', result.id, 'CREATE'); // 8. Return response return NextResponse.json(result); }
React Component Pattern
// src/components/FeatureComponent.tsx 'use client'; import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/Button'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; interface FeatureProps { id: string; initialData?: FeatureData; onComplete?: (result: Result) => void; } export function FeatureComponent({ id, initialData, onComplete }: FeatureProps) { const [data, setData] = useState<FeatureData | null>(initialData ?? null); const [loading, setLoading] = useState(!initialData); const [error, setError] = useState<string | null>(null); useEffect(() => { if (!initialData) { fetchData(); } }, [id]); async function fetchData() { try { setLoading(true); const res = await fetch(`/api/feature/${id}`); if (!res.ok) throw new Error('Failed to fetch'); const data = await res.json(); setData(data); } catch (e) { setError(e instanceof Error ? e.message : 'Unknown error'); } finally { setLoading(false); } } if (loading) { return <div className="animate-pulse">Loading...</div>; } if (error) { return ( <Card className="border-destructive"> <CardContent>Error: {error}</CardContent> </Card> ); } return ( <Card> <CardHeader>Feature Title</CardHeader> <CardContent> {/* Content */} </CardContent> </Card> ); }
Database Query Pattern
// Use secure-db for user data (RLS enforced) import { db, users, checkIns } from '@/db/secure-db'; import { eq, desc } from 'drizzle-orm'; // Get user's own data (RLS automatically filters) async function getUserCheckIns(userId: string) { return db .select() .from(checkIns) .where(eq(checkIns.userId, userId)) .orderBy(desc(checkIns.createdAt)) .limit(30); } // For admin queries, use requireAdmin import { requireAdmin } from '@/db/secure-db'; async function getAdminStats() { const admin = await requireAdmin(); if (!admin) throw new Error('Admin required'); // Now can query across all users return db.select({ count: count() }).from(users); }
Design System
Color Palette (Therapeutic)
/* From globals.css */ --navy: #1a365d; /* Primary - trust, stability */ --teal: #319795; /* Secondary - calm, healing */ --coral: #ed8936; /* Accent - warmth, energy */ --cream: #fffaf0; /* Background - comfort */
Time-Based Themes
function getTimeTheme(): 'dawn' | 'day' | 'dusk' | 'night' { const hour = new Date().getHours(); if (hour >= 5 && hour < 9) return 'dawn'; if (hour >= 9 && hour < 17) return 'day'; if (hour >= 17 && hour < 21) return 'dusk'; return 'night'; }
Component Styling
// Use Tailwind with design tokens <Button className="bg-navy hover:bg-navy/90 text-white" variant="default" > Primary Action </Button> <Card className="bg-cream border-teal/20"> <CardContent className="text-navy"> Therapeutic content </CardContent> </Card>
Testing Pattern
// src/lib/__tests__/feature.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; describe('Feature', () => { beforeEach(() => { vi.clearAllMocks(); }); it('loads and displays data', async () => { vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => ({ data: 'test' }) } as Response); render(<FeatureComponent id="123" />); await waitFor(() => { expect(screen.getByText('test')).toBeInTheDocument(); }); }); it('handles errors gracefully', async () => { vi.mocked(fetch).mockRejectedValueOnce(new Error('Network error')); render(<FeatureComponent id="123" />); await waitFor(() => { expect(screen.getByText(/error/i)).toBeInTheDocument(); }); }); });
Error Handling
// Use structured error responses interface APIError { error: string; code?: string; details?: unknown; } // In API routes return NextResponse.json<APIError>( { error: 'Validation failed', code: 'VALIDATION_ERROR', details: zodError.issues }, { status: 400 } ); // In components try { await submitData(); } catch (e) { if (e instanceof APIError) { toast.error(e.message); } else { toast.error('An unexpected error occurred'); console.error(e); // Log for debugging, not shown to user } }
Environment Variables
Required variables (validated at startup):
# Authentication SESSION_SECRET= # 32+ random characters # AI Integration ANTHROPIC_API_KEY= # Claude API key # Database DATABASE_URL= # SQLite path or connection string # Optional VAPID_PUBLIC_KEY= # Push notifications VAPID_PRIVATE_KEY=
Pre-Commit Checklist
Before committing any changes:
-
passesnpm run lint -
passesnpm run test -
passesnpm run feature:validate - Feature manifest updated (if applicable)
- No secrets in code
- HIPAA compliance maintained (audit logs)
- Accessibility considered (semantic HTML, ARIA)
Common Imports
// Authentication import { getSession, requireAuth } from '@/lib/auth'; // Database import { db } from '@/db/secure-db'; import { eq, desc, and } from 'drizzle-orm'; // Audit import { logPHIAccess, logSecurityEvent } from '@/lib/hipaa/audit'; // Rate limiting import { createRateLimiter } from '@/lib/rate-limit'; // Validation import { z } from 'zod'; // UI Components import { Button } from '@/components/ui/Button'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Input } from '@/components/ui/Input';