Claude-code-plugins anima-sdk-patterns
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/anima-pack/skills/anima-sdk-patterns" ~/.claude/skills/jeremylongshore-claude-code-plugins-anima-sdk-patterns && rm -rf "$T"
manifest:
plugins/saas-packs/anima-pack/skills/anima-sdk-patterns/SKILL.mdsource content
Anima SDK Patterns
Overview
Production patterns for
@animaapp/anima-sdk: singleton client, generation caching, output normalization, and configurable settings presets.
Instructions
Step 1: Singleton Client with Configuration
// src/anima/client.ts import { Anima } from '@animaapp/anima-sdk'; let instance: Anima | null = null; export function getAnimaClient(): Anima { if (!instance) { if (!process.env.ANIMA_TOKEN) throw new Error('ANIMA_TOKEN not set'); instance = new Anima({ auth: { token: process.env.ANIMA_TOKEN } }); } return instance; } // Preset configurations for different project needs export const PRESETS = { nextjs: { language: 'typescript' as const, framework: 'react' as const, styling: 'tailwind' as const, uiLibrary: 'shadcn' as const }, vite: { language: 'typescript' as const, framework: 'react' as const, styling: 'tailwind' as const }, vue: { language: 'typescript' as const, framework: 'vue' as const, styling: 'tailwind' as const }, static: { language: 'javascript' as const, framework: 'html' as const, styling: 'css' as const }, } as const;
Step 2: Generation Cache
// src/anima/cache.ts import crypto from 'crypto'; import fs from 'fs'; interface CacheEntry { files: Array<{ fileName: string; content: string }>; generatedAt: string; settingsHash: string; } class AnimaCache { private cacheDir: string; constructor(cacheDir: string = '.anima-cache') { this.cacheDir = cacheDir; fs.mkdirSync(cacheDir, { recursive: true }); } private getKey(fileKey: string, nodeId: string, settings: object): string { const hash = crypto.createHash('md5') .update(`${fileKey}:${nodeId}:${JSON.stringify(settings)}`) .digest('hex'); return hash; } get(fileKey: string, nodeId: string, settings: object): CacheEntry | null { const key = this.getKey(fileKey, nodeId, settings); const path = `${this.cacheDir}/${key}.json`; if (!fs.existsSync(path)) return null; return JSON.parse(fs.readFileSync(path, 'utf8')); } set(fileKey: string, nodeId: string, settings: object, files: any[]): void { const key = this.getKey(fileKey, nodeId, settings); const entry: CacheEntry = { files, generatedAt: new Date().toISOString(), settingsHash: key, }; fs.writeFileSync(`${this.cacheDir}/${key}.json`, JSON.stringify(entry)); } } export { AnimaCache };
Step 3: Output Normalizer
// src/anima/normalizer.ts // Normalize Anima output to match project conventions interface NormalizationConfig { componentNameCase: 'PascalCase' | 'kebab-case'; addBarrelExport: boolean; wrapWithCn: boolean; addTypeAnnotations: boolean; } function normalizeOutput( files: Array<{ fileName: string; content: string }>, config: NormalizationConfig, ): Array<{ fileName: string; content: string }> { return files.map(file => { let content = file.content; if (config.wrapWithCn && file.fileName.endsWith('.tsx')) { // Add cn() import and wrap className strings if (!content.includes("import { cn }")) { content = content.replace( /^(import .+\n)/m, "$1import { cn } from '@/lib/utils';\n" ); } } if (config.addTypeAnnotations && file.fileName.endsWith('.tsx')) { content = content.replace( /export default function (\w+)\(\)/g, 'export default function $1(): React.ReactElement' ); } return { fileName: file.fileName, content }; }); } export { normalizeOutput, NormalizationConfig };
Step 4: Error Recovery Pattern
// src/anima/retry.ts async function generateWithRetry( anima: Anima, params: any, maxRetries: number = 3, ): Promise<any> { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await anima.generateCode(params); } catch (err: any) { if (attempt === maxRetries) throw err; const delay = 2000 * Math.pow(2, attempt - 1); console.log(`Generation failed, retry ${attempt}/${maxRetries} in ${delay}ms`); await new Promise(r => setTimeout(r, delay)); } } }
Output
- Singleton client with preset configurations
- File-based generation cache (avoid redundant API calls)
- Output normalizer for project convention matching
- Retry pattern for API resilience
Resources
Next Steps
Apply patterns in
anima-core-workflow-a for automated design pipelines.