From 3cb8d3cb3fe68ac33efe31875dc3a7ef18f592cf Mon Sep 17 00:00:00 2001 From: DMleadgen Date: Fri, 6 Mar 2026 16:03:31 -0700 Subject: [PATCH] Initial commit: AI Ops Templates repository - Schema.org JSON-LD templates (product, event, local-business, faq) - Brand, UI, SEO, and decision guide rules - Working code snippets (vendor-card, schema-inject, deploy-webhook) - JSON schemas for project config validation - Client presets (slc-bride, default) - Self-update protocol with changelog tracking Made-with: Cursor --- .cursor/rules/ai-context-update.md | 128 ++++++++++++++ README.md | 108 ++++++++++++ context/CHANGELOG.md | 52 ++++++ context/last-sync.json | 42 +++++ presets/default.json | 88 ++++++++++ presets/slc-bride.json | 100 +++++++++++ rules/ai-decision-guide.md | 166 ++++++++++++++++++ rules/common-fixes.md | 208 +++++++++++++++++++++++ rules/seo-rules.md | 115 +++++++++++++ rules/tailwind-brand.json | 68 ++++++++ rules/ui-fixes.json | 58 +++++++ schemas/ai-cli.schema.json | 113 ++++++++++++ schemas/ai-context.schema.json | 119 +++++++++++++ skill-templates/client-skill.template.md | 173 +++++++++++++++++++ snippets/add-internal-link.tsx | 39 +++++ snippets/alt-text-placeholder.go | 49 ++++++ snippets/breadcrumb.tsx | 78 +++++++++ snippets/deploy-webhook.sh | 134 +++++++++++++++ snippets/schema-inject.ts | 99 +++++++++++ snippets/vendor-card.tsx | 92 ++++++++++ templates/schema-event.json | 45 +++++ templates/schema-faq.json | 35 ++++ templates/schema-local-business.json | 59 +++++++ templates/schema-product.json | 34 ++++ 24 files changed, 2202 insertions(+) create mode 100644 .cursor/rules/ai-context-update.md create mode 100644 README.md create mode 100644 context/CHANGELOG.md create mode 100644 context/last-sync.json create mode 100644 presets/default.json create mode 100644 presets/slc-bride.json create mode 100644 rules/ai-decision-guide.md create mode 100644 rules/common-fixes.md create mode 100644 rules/seo-rules.md create mode 100644 rules/tailwind-brand.json create mode 100644 rules/ui-fixes.json create mode 100644 schemas/ai-cli.schema.json create mode 100644 schemas/ai-context.schema.json create mode 100644 skill-templates/client-skill.template.md create mode 100644 snippets/add-internal-link.tsx create mode 100644 snippets/alt-text-placeholder.go create mode 100644 snippets/breadcrumb.tsx create mode 100644 snippets/deploy-webhook.sh create mode 100644 snippets/schema-inject.ts create mode 100644 snippets/vendor-card.tsx create mode 100644 templates/schema-event.json create mode 100644 templates/schema-faq.json create mode 100644 templates/schema-local-business.json create mode 100644 templates/schema-product.json diff --git a/.cursor/rules/ai-context-update.md b/.cursor/rules/ai-context-update.md new file mode 100644 index 0000000..54ce286 --- /dev/null +++ b/.cursor/rules/ai-context-update.md @@ -0,0 +1,128 @@ +# AI Context Update Rule + +This rule defines when and how context files must be updated to prevent drift. + +--- + +## MANDATORY: Update Context After These Events + +When ANY of the following occur, you MUST update the relevant context files BEFORE completing the task: + +### 1. New Feature Added +**Update**: Client's SKILL.md with new patterns +**File**: `.cursor/skills/{{CLIENT}}/SKILL.md` +**Section**: Add to "Recent Changes" and relevant feature sections + +### 2. Brand Colors/Fonts Changed +**Update**: Presets file +**File**: `ai-ops-templates/presets/{{CLIENT}}.json` +**Section**: `brand.colors` or `brand.fonts` + +### 3. New API Endpoint Created +**Update**: Project config and context manifest +**Files**: +- `.ai-cli.json` under `apis` section +- `.ai-context.json` under `api_endpoints` + +### 4. New Database Table/Column +**Update**: Context manifest and relevant skill file +**Files**: +- `.ai-context.json` under `database_schema.tables` +- `.cursor/skills/{{CLIENT}}/SKILL.md` under "Database Schema" + +### 5. New Component Pattern +**Update**: Add working example to snippets +**File**: `ai-ops-templates/snippets/{{COMPONENT_NAME}}.tsx` +**Include**: Full working code with usage comments + +### 6. Bug Fix Revealed New Pattern +**Update**: Common fixes documentation +**File**: `ai-ops-templates/rules/common-fixes.md` +**Include**: Symptom, cause, solution, and code example + +### 7. New MCP Tool Added +**Update**: Decision guide +**File**: `ai-ops-templates/rules/ai-decision-guide.md` +**Section**: "Tool Selection Quick Reference" + +--- + +## Update Command Pattern + +After making changes, ALWAYS ask the user: + +> "Should I update the AI context files to reflect these changes?" + +If **yes**, update: +1. The specific file that changed +2. The `CHANGELOG.md` with date and summary +3. The `_meta.last_updated` timestamp in the changed file +4. Any dependent files that reference the changed file + +--- + +## Change Log Format + +Every context file must include a `_meta` section: + +```json +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "updated_by": "ai-agent", + "change_summary": "Description of what changed" + } +} +``` + +For Markdown files, use this footer format: + +```markdown +--- +## Context Metadata (Auto-Updated) +_last_updated: 2026-03-06 +_version: 1.0.0 +_synced_from: git.abundancepartners.app/abundance/ai-ops-templates +``` + +--- + +## Files That Require _meta Sections + +| File Type | _meta Required | +|-----------|----------------| +| JSON configs | Yes | +| JSON schemas | Yes | +| TypeScript snippets | No (use comments) | +| Shell scripts | No (use comments) | +| Markdown files | Yes (footer format) | + +--- + +## Example Update Workflow + +``` +User: "Add a new vendor search feature" + +AI Actions: +1. Implement the feature +2. Ask: "Should I update the AI context files?" +3. If yes: + a. Add to snippets/vendor-search.tsx + b. Update .ai-context.json with new API endpoint + c. Update SKILL.md with new pattern + d. Update CHANGELOG.md + e. Update last-sync.json timestamps +``` + +--- + +## Never Skip Context Updates + +Context that isn't updated becomes actively harmful: +- Wrong patterns get repeated +- Time is wasted re-explaining the same things +- AI hallucinations increase + +Always update context. It takes 30 seconds but saves hours later. diff --git a/README.md b/README.md new file mode 100644 index 0000000..702edf5 --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# AI Ops Templates + +A curated repository of templates, rules, snippets, and presets for AI-assisted development across all Abundance client projects. + +## Purpose + +This repository provides structured context that enables AI agents (like Cursor's Composer) to: +- Build features 10x faster with accurate brand/style context +- Follow established patterns without re-explanation +- Maintain consistency across all client projects +- Self-update context to prevent drift + +## Structure + +``` +ai-ops-templates/ +├── templates/ # Schema.org JSON-LD templates +│ ├── schema-product.json +│ ├── schema-event.json +│ ├── schema-local-business.json +│ └── schema-faq.json +├── rules/ # Development rules and guidelines +│ ├── tailwind-brand.json +│ ├── ui-fixes.json +│ ├── seo-rules.md +│ ├── common-fixes.md +│ └── ai-decision-guide.md +├── snippets/ # Reusable code components +│ ├── add-internal-link.tsx +│ ├── alt-text-placeholder.go +│ ├── breadcrumb.tsx +│ ├── vendor-card.tsx +│ ├── schema-inject.ts +│ └── deploy-webhook.sh +├── schemas/ # JSON schemas for validation +│ ├── ai-cli.schema.json +│ └── ai-context.schema.json +├── presets/ # Client brand presets +│ ├── slc-bride.json +│ └── default.json +├── context/ # Context tracking +│ ├── CHANGELOG.md +│ └── last-sync.json +├── skill-templates/ # Templates for client SKILL.md files +│ └── client-skill.template.md +└── .cursor/rules/ # Cursor-specific rules + └── ai-context-update.md +``` + +## Usage + +### For AI Agents + +1. Read `.ai-context.json` in the project root first +2. Reference `presets/.json` for brand rules +3. Use `snippets/` for component patterns +4. Check `rules/ai-decision-guide.md` for tool selection +5. Update context after making changes (see `.cursor/rules/ai-context-update.md`) + +### For Humans + +1. Copy `presets/default.json` to create new client presets +2. Use `skill-templates/client-skill.template.md` for new client SKILL.md files +3. Update `CHANGELOG.md` when making changes +4. Run `context/last-sync.json` updates via the MCP tool + +## Per-Project Setup + +Each client project needs: + +1. **`.ai-cli.json`** - Project configuration (use `schemas/ai-cli.schema.json`) +2. **`.ai-context.json`** - Context manifest (use `schemas/ai-context.schema.json`) +3. **`.cursor/skills/{client}/SKILL.md`** - Client-specific skill file + +## Self-Update Protocol + +This repository uses a self-update protocol to prevent context drift. See `.cursor/rules/ai-context-update.md` for details. + +Key triggers for updates: +- New feature added +- Brand changes +- New API endpoints +- Database schema changes +- Bug fixes that reveal patterns + +## Adding New Clients + +1. Create preset: `presets/{client-slug}.json` +2. Create skill file: `.cursor/skills/{client-slug}/SKILL.md` +3. Add to project: `.ai-cli.json` and `.ai-context.json` +4. Update this README if needed + +## Repository URL + +``` +https://git.abundancepartners.app/abundance/ai-ops-templates +``` + +Raw file access: +``` +https://git.abundancepartners.app/abundance/ai-ops-templates/raw/main/{path} +``` + +## Version + +- **Version**: 1.0.0 +- **Last Updated**: 2026-03-06 +- **Maintained By**: AI Agent (auto-updated) diff --git a/context/CHANGELOG.md b/context/CHANGELOG.md new file mode 100644 index 0000000..d26d562 --- /dev/null +++ b/context/CHANGELOG.md @@ -0,0 +1,52 @@ +# AI Context Changelog + +This file tracks all changes to the ai-ops-templates repository. Updates are logged automatically when context files are modified. + +--- + +## Format + +``` +## [YYYY-MM-DD] - Version X.X.X + +### Added +- New features or files + +### Changed +- Modifications to existing files + +### Fixed +- Bug fixes or corrections + +### Context Updates +- Changes triggered by project work +``` + +--- + +## [2026-03-06] - Version 1.0.0 + +### Added +- Initial repository structure +- templates/ - Schema.org JSON-LD templates (product, event, local-business, faq) +- rules/ - Brand, UI, SEO, and decision guide rules +- snippets/ - Reusable code components and utilities +- schemas/ - JSON schemas for configuration validation +- presets/ - Client brand presets (slc-bride, default) +- context/ - Changelog and sync tracking +- skill-templates/ - Client SKILL.md template + +### Architecture +- Self-update protocol implemented +- Meta sections in all JSON files for version tracking +- Decision guide for AI tool/pattern selection + +--- + + diff --git a/context/last-sync.json b/context/last-sync.json new file mode 100644 index 0000000..c2488a4 --- /dev/null +++ b/context/last-sync.json @@ -0,0 +1,42 @@ +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "description": "Timestamp tracking for context synchronization" + }, + "last_sync": "2026-03-06T15:52:00Z", + "repository_url": "https://git.abundancepartners.app/abundance/ai-ops-templates", + "branch": "main", + "commit_hash": "initial", + "files": { + "templates": { + "schema-product.json": "2026-03-06T15:52:00Z", + "schema-event.json": "2026-03-06T15:52:00Z", + "schema-local-business.json": "2026-03-06T15:52:00Z", + "schema-faq.json": "2026-03-06T15:52:00Z" + }, + "rules": { + "tailwind-brand.json": "2026-03-06T15:52:00Z", + "ui-fixes.json": "2026-03-06T15:52:00Z", + "seo-rules.md": "2026-03-06T15:52:00Z", + "common-fixes.md": "2026-03-06T15:52:00Z", + "ai-decision-guide.md": "2026-03-06T15:52:00Z" + }, + "snippets": { + "add-internal-link.tsx": "2026-03-06T15:52:00Z", + "alt-text-placeholder.go": "2026-03-06T15:52:00Z", + "breadcrumb.tsx": "2026-03-06T15:52:00Z", + "vendor-card.tsx": "2026-03-06T15:52:00Z", + "schema-inject.ts": "2026-03-06T15:52:00Z", + "deploy-webhook.sh": "2026-03-06T15:52:00Z" + }, + "presets": { + "slc-bride.json": "2026-03-06T15:52:00Z", + "default.json": "2026-03-06T15:52:00Z" + }, + "schemas": { + "ai-cli.schema.json": "2026-03-06T15:52:00Z", + "ai-context.schema.json": "2026-03-06T15:52:00Z" + } + } +} diff --git a/presets/default.json b/presets/default.json new file mode 100644 index 0000000..d7a0483 --- /dev/null +++ b/presets/default.json @@ -0,0 +1,88 @@ +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "description": "Default brand preset - neutral starting point for new clients" + }, + "client": "default", + "site_url": "{{SITE_URL}}", + "brand": { + "name": "{{BRAND_NAME}}", + "tagline": "{{TAGLINE}}", + "colors": { + "primary": { + "50": "#f0f9ff", + "100": "#e0f2fe", + "200": "#bae6fd", + "300": "#7dd3fc", + "400": "#38bdf8", + "500": "#0ea5e9", + "600": "#0284c7", + "700": "#0369a1", + "800": "#075985", + "900": "#0c4a6e", + "950": "#082f49" + }, + "secondary": { + "50": "#f8fafc", + "100": "#f1f5f9", + "200": "#e2e8f0", + "300": "#cbd5e1", + "400": "#94a3b8", + "500": "#64748b", + "600": "#475569", + "700": "#334155", + "800": "#1e293b", + "900": "#0f172a", + "950": "#020617" + }, + "background": "#ffffff", + "foreground": "#0f172a", + "muted": "#f1f5f9", + "mutedForeground": "#64748b", + "card": "#ffffff", + "cardForeground": "#0f172a", + "popover": "#ffffff", + "popoverForeground": "#0f172a", + "border": "#e2e8f0", + "input": "#e2e8f0", + "ring": "#0ea5e9", + "destructive": "#ef4444", + "destructiveForeground": "#fafafa" + }, + "fonts": { + "heading": "Inter", + "body": "Inter", + "mono": "JetBrains Mono" + }, + "borderRadius": { + "sm": "0.25rem", + "md": "0.5rem", + "lg": "0.75rem", + "xl": "1rem" + }, + "spacing": { + "section": "4rem", + "container": "1280px" + } + }, + "seo": { + "title_template": "{page} | {{BRAND_NAME}}", + "default_title": "{{SEO_TITLE}}", + "default_description": "{{SEO_DESCRIPTION}}", + "schema_types": ["LocalBusiness", "Product"], + "social": { + "facebook": "{{FACEBOOK_URL}}", + "instagram": "{{INSTAGRAM_URL}}", + "twitter": "{{TWITTER_URL}}" + } + }, + "infrastructure": { + "server": "{{SERVER_NAME}}", + "server_ip": "{{SERVER_IP}}", + "database": "{{DATABASE_NAME}}", + "coolify_url": "https://app.abundancepartners.app" + }, + "features": {}, + "routes": {} +} diff --git a/presets/slc-bride.json b/presets/slc-bride.json new file mode 100644 index 0000000..c04be58 --- /dev/null +++ b/presets/slc-bride.json @@ -0,0 +1,100 @@ +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "description": "SLC Bride brand preset - Salt Lake City wedding vendor directory" + }, + "client": "slc-bride", + "site_url": "https://saltlakebride.com", + "brand": { + "name": "Salt Lake Bride", + "tagline": "Your Complete Salt Lake City Wedding Resource", + "colors": { + "primary": { + "50": "#fdf2f8", + "100": "#fce7f3", + "200": "#fbcfe8", + "300": "#f9a8d4", + "400": "#f472b6", + "500": "#ec4899", + "600": "#db2777", + "700": "#be185d", + "800": "#9d174d", + "900": "#831843", + "950": "#500724" + }, + "secondary": { + "50": "#faf5ff", + "100": "#f3e8ff", + "200": "#e9d5ff", + "300": "#d8b4fe", + "400": "#c084fc", + "500": "#a855f7", + "600": "#9333ea", + "700": "#7e22ce", + "800": "#6b21a8", + "900": "#581c87", + "950": "#3b0764" + }, + "background": "#ffffff", + "foreground": "#1a1a2e", + "muted": "#f4f4f5", + "mutedForeground": "#71717a", + "card": "#ffffff", + "cardForeground": "#1a1a2e", + "popover": "#ffffff", + "popoverForeground": "#1a1a2e", + "border": "#e4e4e7", + "input": "#e4e4e7", + "ring": "#ec4899", + "destructive": "#ef4444", + "destructiveForeground": "#fafafa" + }, + "fonts": { + "heading": "Playfair Display", + "body": "Inter", + "mono": "JetBrains Mono" + }, + "borderRadius": { + "sm": "0.25rem", + "md": "0.5rem", + "lg": "0.75rem", + "xl": "1rem" + }, + "spacing": { + "section": "4rem", + "container": "1280px" + } + }, + "seo": { + "title_template": "{page} | Salt Lake Bride", + "default_title": "Salt Lake Bride - Your Complete Salt Lake City Wedding Resource", + "default_description": "Find the best wedding vendors, venues, and resources in Salt Lake City. Browse photographers, florists, caterers, and more for your perfect Utah wedding.", + "schema_types": ["LocalBusiness", "Event", "FAQPage"], + "social": { + "facebook": "https://facebook.com/saltlakebride", + "instagram": "https://instagram.com/saltlakebride", + "pinterest": "https://pinterest.com/saltlakebride" + } + }, + "infrastructure": { + "server": "slcbride", + "server_ip": "89.117.22.126", + "database": "slcbride", + "coolify_url": "https://app.abundancepartners.app" + }, + "features": { + "vendor_directory": true, + "venue_listings": true, + "blog": true, + "real_weddings": true, + "planning_tools": true + }, + "routes": { + "vendors": "/vendors", + "venues": "/venues", + "blog": "/blog", + "real_weddings": "/real-weddings", + "planning": "/planning" + } +} diff --git a/rules/ai-decision-guide.md b/rules/ai-decision-guide.md new file mode 100644 index 0000000..5cf9df7 --- /dev/null +++ b/rules/ai-decision-guide.md @@ -0,0 +1,166 @@ +# AI Decision Guide + +This document maps common scenarios to the correct tools and patterns. + +--- + +## Adding a New Page + +1. Check `.ai-context.json` for route patterns +2. Read brand preset from `presets/.json` +3. Copy structure from `snippets/` similar pages +4. Add schema from `templates/` if SEO-relevant +5. Follow heading hierarchy from `rules/seo-rules.md` + +--- + +## Deploying Changes + +### ALWAYS follow this sequence: + +```bash +# Step 1: Deploy to staging +deploy_push({ target: "staging", app_id: "" }) + +# Step 2: Verify on staging URL +# Check: functionality, styling, data loading + +# Step 3: If successful, deploy to prod +deploy_push({ target: "prod", app_id: "" }) +``` + +### Never skip staging deployment for production changes. + +--- + +## Fixing Brand/Style Issues + +1. Read `presets/.json` for brand rules +2. Check `rules/ui-fixes.json` for patterns +3. Use shadcn MCP for component updates +4. Verify dark mode support if applicable + +### Common Patterns: +| Issue | Solution | +|-------|----------| +| Colors not matching | Check CSS variables match preset | +| Font not loading | Verify Google Fonts import | +| Spacing inconsistent | Use Tailwind spacing scale | + +--- + +## Adding Structured Data + +1. Fetch template: `seo_add_schema({ template: "product", data: {...} })` +2. Template pulled from Forgejo with current brand +3. Inject into page `` as JSON-LD script tag +4. Validate with Google Rich Results Test + +### Template Selection: +| Content Type | Template | +|--------------|----------| +| Product/Service | `schema-product.json` | +| Event | `schema-event.json` | +| Business Listing | `schema-local-business.json` | +| FAQ Section | `schema-faq.json` | + +--- + +## Database Operations + +1. Read `.ai-cli.json` for database connection info +2. Use `supabase_query` with parameterized queries only +3. **NEVER** use string concatenation for queries +4. Check RLS policies if query fails + +### Safe Query Pattern: +```javascript +// Correct +supabase_query({ + query: "SELECT * FROM vendors WHERE category = $1 AND active = $2", + params: ["photography", true] +}) + +// WRONG - Never do this +supabase_query({ + query: `SELECT * FROM vendors WHERE category = '${category}'` +}) +``` + +--- + +## Creating New Components + +1. Check `snippets/` for existing similar components +2. Follow brand preset for styling +3. Use shadcn/ui as base when possible +4. Add accessibility attributes (see `rules/ui-fixes.json`) +5. After creation, update context: + ``` + context_update({ + file: "snippets/components.md", + section: "new-components", + content: { name: "ComponentName", path: "..." } + }) + ``` + +--- + +## Handling Errors + +### Step 1: Identify Error Type +| Error Type | Check | +|------------|-------| +| Type Error | TypeScript interfaces, props | +| Runtime Error | Console logs, stack trace | +| Build Error | Dependencies, env vars | +| Database Error | RLS policies, query syntax | + +### Step 2: Check Common Fixes +Look in `rules/common-fixes.md` for known solutions + +### Step 3: If New Pattern Found +1. Document the fix +2. Update `rules/common-fixes.md` +3. Run `context_update` to log the change + +--- + +## Context Update Required When + +| Event | Action | +|-------|--------| +| New feature added | Update client's SKILL.md | +| Bug fix discovered | Add to `rules/common-fixes.md` | +| Brand changed | Update `presets/.json` | +| New component pattern | Add to `snippets/` | +| New API endpoint | Add to `.ai-cli.json` | +| Database schema change | Update relevant schemas | + +### After ANY context update: +1. Update `context/CHANGELOG.md` +2. Update `last_updated` in `_meta` section +3. Notify user if major change + +--- + +## Tool Selection Quick Reference + +| Task | Tool | +|------|------| +| Check MCP connection | `ping()` | +| Deploy app | `deploy_push()` | +| Add SEO schema | `seo_add_schema()` | +| Query database | `supabase_query()` | +| Update context files | `context_update()` | +| UI components/themes | shadcn MCP | +| File operations | filesystem MCP | + +--- + +## Emergency Contacts + +If unable to resolve: +1. Check server logs: `abundance docker logs -f --tail 100` +2. Check health: `abundance health check` +3. Escalate with full error context diff --git a/rules/common-fixes.md b/rules/common-fixes.md new file mode 100644 index 0000000..beee448 --- /dev/null +++ b/rules/common-fixes.md @@ -0,0 +1,208 @@ +# Common Fixes + +This document tracks recurring issues and their solutions. Update when new patterns are discovered. + +--- + +## React/Next.js Issues + +### Hydration Mismatch +**Symptom:** "Hydration failed because the server rendered HTML didn't match the client" + +**Solution:** +```tsx +// Use useEffect for client-only rendering +const [mounted, setMounted] = useState(false) +useEffect(() => setMounted(true), []) +if (!mounted) return null +``` + +### Event Handler Type Errors +**Symptom:** Type errors on onClick handlers + +**Solution:** +```tsx +// Always type the event parameter +onClick={(e: React.MouseEvent) => handleClick(e)} +// For forms +onSubmit={(e: React.FormEvent) => handleSubmit(e)} +``` + +### State Not Updating +**Symptom:** State updates not reflecting in UI + +**Solution:** +```tsx +// For arrays/objects, always create new references +setItems([...items, newItem]) +setUser({ ...user, name: 'New Name' }) +``` + +--- + +## Tailwind CSS Issues + +### Dark Mode Not Working +**Symptom:** Dark mode classes not applying + +**Solution:** +```js +// tailwind.config.js +module.exports = { + darkMode: 'class', // or 'media' +} +``` + +### Custom Colors Not Applying +**Symptom:** Custom color classes not generating + +**Solution:** +```js +// tailwind.config.js - extend, don't replace +module.exports = { + theme: { + extend: { + colors: { + primary: 'var(--primary)', + } + } + } +} +``` + +--- + +## Form Issues + +### Zod Validation Not Triggering +**Symptom:** Form submits with invalid data + +**Solution:** +```tsx +// Ensure resolver is passed correctly +const form = useForm({ + resolver: zodResolver(formSchema), + mode: 'onChange', // or 'onBlur', 'all' +}) +``` + +### React Hook Form Not Re-rendering +**Symptom:** Form values update but UI doesn't + +**Solution:** +```tsx +// Use watch or useWatch for reactive updates +const watchedValue = form.watch('fieldName') +``` + +--- + +## Database/Supabase Issues + +### RLS Policy Blocking Queries +**Symptom:** "new row violates row-level security policy" + +**Solution:** +```sql +-- Check policies on table +SELECT * FROM pg_policies WHERE tablename = 'your_table'; + +-- Add policy for authenticated users +CREATE POLICY "Users can insert own data" +ON your_table FOR INSERT +WITH CHECK (auth.uid() = user_id); +``` + +### Connection Pool Exhausted +**Symptom:** "remaining connection slots are reserved" + +**Solution:** +- Use Supabase connection pooling (port 6543) +- Close connections properly +- Use transaction mode for serverless + +--- + +## Deployment Issues + +### Build Fails on Server +**Symptom:** Works locally but fails in production + +**Common Causes:** +1. Environment variables missing +2. Node version mismatch +3. Missing devDependencies in production + +**Solution:** +```bash +# Check Node version +node --version + +# Verify env vars are set +# Add to Coolify environment variables +``` + +### Container Health Check Failing +**Symptom:** Container restarts repeatedly + +**Solution:** +```yaml +# docker-compose.yaml - increase health check interval +healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +``` + +--- + +## Type Errors + +### "Type X is not assignable to type Y" +**Symptom:** TypeScript errors in component props + +**Solution:** +```tsx +// Use proper type imports +import type { SomeType } from './types' + +// For component props +interface ComponentProps { + data: SomeType + onClick?: () => void +} +``` + +--- + +## Performance Issues + +### Slow Page Load +**Diagnosis Steps:** +1. Check Network tab for large payloads +2. Use Lighthouse for audit +3. Check for unnecessary re-renders + +**Quick Fixes:** +```tsx +// Memoize expensive computations +const memoizedValue = useMemo(() => computeExpensive(a, b), [a, b]) + +// Memoize components +const MemoizedComponent = memo(ExpensiveComponent) + +// Code split +const HeavyComponent = lazy(() => import('./HeavyComponent')) +``` + +--- + +## Update Log + +| Date | Issue | Solution Added | +|------|-------|----------------| +| 2026-03-06 | Initial | Created document | + +**To add new fixes:** Update this file and increment version in _meta diff --git a/rules/seo-rules.md b/rules/seo-rules.md new file mode 100644 index 0000000..24e80b2 --- /dev/null +++ b/rules/seo-rules.md @@ -0,0 +1,115 @@ +# SEO Rules + +## Internal Linking + +### Link Patterns +- Link to relevant vendor/category pages from content +- Use descriptive anchor text (avoid "click here") +- Link to related services/products where contextually relevant +- Maximum 100 internal links per page + +### URL Structure +- Use kebab-case for URLs: `/vendors/wedding-photography` +- Include location where relevant: `/venues/salt-lake-city/hotels` +- Avoid query parameters for canonical content + +### Anchor Text Guidelines +- Include target keywords naturally +- Match the linked page's primary topic +- Keep under 60 characters +- Examples: + - Good: "wedding photography packages" + - Bad: "click here for more info" + +--- + +## Alt Text + +### Format +``` +[Subject] + [Context/Action] + [Relevance to page content] +``` + +### Examples +- Product image: "White wedding dress with lace details at Salt Lake Bride bridal expo" +- Vendor photo: "Portrait of wedding photographer Sarah Smith at outdoor ceremony" +- Venue image: "Mountain view ceremony space at The Grand America Hotel Salt Lake City" + +### Rules +1. Be descriptive but concise (125 characters max) +2. Include relevant keywords naturally +3. Don't start with "Image of" or "Picture of" +4. Describe what's visible, not decorative elements +5. For decorative images, use empty alt="" + +--- + +## Meta Descriptions + +### Format +- 150-160 characters +- Include primary keyword near beginning +- Include call-to-action when appropriate +- Match page content accurately + +### Template +``` +Discover {{SERVICE}} in {{LOCATION}}. {{UNIQUE_VALUE_PROPOSITION}}. Browse {{COUNT}} options on {{SITE_NAME}}. +``` + +--- + +## Schema Markup + +### Required Schemas by Page Type +| Page Type | Required Schema | +|-----------|----------------| +| Vendor listing | LocalBusiness | +| Product page | Product | +| Event page | Event | +| FAQ page | FAQPage | +| Article | Article | +| Homepage | Organization + WebSite | + +### Implementation +- Use JSON-LD format in `` +- Include all required properties +- Validate with Google Rich Results Test + +--- + +## Heading Hierarchy + +### Rules +1. One H1 per page (main page title) +2. H2 for major sections +3. H3 for subsections +4. Never skip heading levels +5. Include keywords naturally in headings + +### Example Structure +``` +H1: Wedding Photographers in Salt Lake City + H2: Top Rated Photographers + H3: Portrait Photography + H3: Documentary Style + H2: How to Choose Your Photographer + H3: Budget Considerations + H3: Style Matching +``` + +--- + +## Image Optimization + +### Technical Requirements +- Format: WebP with JPEG fallback +- Max file size: 200KB for hero, 100KB for thumbnails +- Use responsive images with srcset +- Lazy load images below the fold + +### Naming Convention +``` +[category]-[subject]-[detail]-[location].webp +Example: venue-ballroom-crystal-chandeliers-salt-lake-city.webp +``` diff --git a/rules/tailwind-brand.json b/rules/tailwind-brand.json new file mode 100644 index 0000000..ee9c56f --- /dev/null +++ b/rules/tailwind-brand.json @@ -0,0 +1,68 @@ +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "description": "Tailwind brand configuration template - customize per client" + }, + "colors": { + "primary": { + "50": "{{PRIMARY_50}}", + "100": "{{PRIMARY_100}}", + "200": "{{PRIMARY_200}}", + "300": "{{PRIMARY_300}}", + "400": "{{PRIMARY_400}}", + "500": "{{PRIMARY_500}}", + "600": "{{PRIMARY_600}}", + "700": "{{PRIMARY_700}}", + "800": "{{PRIMARY_800}}", + "900": "{{PRIMARY_900}}", + "950": "{{PRIMARY_950}}" + }, + "secondary": { + "50": "{{SECONDARY_50}}", + "100": "{{SECONDARY_100}}", + "200": "{{SECONDARY_200}}", + "300": "{{SECONDARY_300}}", + "400": "{{SECONDARY_400}}", + "500": "{{SECONDARY_500}}", + "600": "{{SECONDARY_600}}", + "700": "{{SECONDARY_700}}", + "800": "{{SECONDARY_800}}", + "900": "{{SECONDARY_900}}", + "950": "{{SECONDARY_950}}" + }, + "background": "{{BACKGROUND}}", + "foreground": "{{FOREGROUND}}", + "muted": "{{MUTED}}", + "mutedForeground": "{{MUTED_FOREGROUND}}", + "card": "{{CARD}}", + "cardForeground": "{{CARD_FOREGROUND}}", + "popover": "{{POPOVER}}", + "popoverForeground": "{{POPOVER_FOREGROUND}}", + "border": "{{BORDER}}", + "input": "{{INPUT}}", + "ring": "{{RING}}", + "destructive": "{{DESTRUCTIVE}}", + "destructiveForeground": "{{DESTRUCTIVE_FOREGROUND}}" + }, + "fonts": { + "heading": "{{HEADING_FONT}}", + "body": "{{BODY_FONT}}", + "mono": "{{MONO_FONT}}" + }, + "borderRadius": { + "sm": "{{RADIUS_SM}}", + "md": "{{RADIUS_MD}}", + "lg": "{{RADIUS_LG}}", + "xl": "{{RADIUS_XL}}" + }, + "spacing": { + "section": "{{SECTION_SPACING}}", + "container": "{{CONTAINER_MAX_WIDTH}}" + }, + "shadows": { + "sm": "{{SHADOW_SM}}", + "md": "{{SHADOW_MD}}", + "lg": "{{SHADOW_LG}}" + } +} diff --git a/rules/ui-fixes.json b/rules/ui-fixes.json new file mode 100644 index 0000000..04f6134 --- /dev/null +++ b/rules/ui-fixes.json @@ -0,0 +1,58 @@ +{ + "_meta": { + "last_updated": "2026-03-06T15:52:00Z", + "version": "1.0.0", + "description": "Common UI fixes and patterns for consistent implementation" + }, + "eventHandling": { + "description": "Use onClick handlers with proper event typing", + "pattern": "onClick={(e: React.MouseEvent) => handleClick(e)}", + "avoid": "onClick={handleClick()} or onClick={() => handleClick}" + }, + "darkMode": { + "description": "Use CSS variables for dark mode support", + "pattern": "className=\"bg-background text-foreground dark:bg-background-dark dark:text-foreground-dark\"", + "cssVariables": [ + "--background", + "--foreground", + "--muted", + "--muted-foreground", + "--card", + "--card-foreground" + ], + "tailwindConfig": { + "darkMode": "class" + } + }, + "responsiveDesign": { + "mobileFirst": true, + "breakpoints": { + "sm": "640px", + "md": "768px", + "lg": "1024px", + "xl": "1280px", + "2xl": "1536px" + }, + "pattern": "className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3\"" + }, + "accessibility": { + "description": "Required accessibility attributes", + "rules": [ + "Always include alt text for images", + "Use aria-label for icon-only buttons", + "Ensure color contrast ratio >= 4.5:1", + "Use semantic HTML elements", + "Include focus states for interactive elements" + ] + }, + "formHandling": { + "description": "React Hook Form patterns with Zod validation", + "pattern": "const form = useForm({ resolver: zodResolver(formSchema) })", + "validation": "Use Zod schemas for both client and server validation" + }, + "loadingStates": { + "skeleton": "Use Skeleton component from shadcn/ui", + "spinner": "Use Loader2 icon with animate-spin", + "disabled": "Disable buttons during async operations: disabled={isSubmitting}" + } +} diff --git a/schemas/ai-cli.schema.json b/schemas/ai-cli.schema.json new file mode 100644 index 0000000..2185d95 --- /dev/null +++ b/schemas/ai-cli.schema.json @@ -0,0 +1,113 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://git.abundancepartners.app/abundance/ai-ops-templates/schemas/ai-cli.schema.json", + "title": "AI CLI Configuration", + "description": "Schema for per-project AI CLI configuration", + "type": "object", + "required": ["project", "infrastructure"], + "properties": { + "_meta": { + "type": "object", + "properties": { + "last_updated": { "type": "string", "format": "date-time" }, + "version": { "type": "string" }, + "updated_by": { "type": "string" }, + "change_summary": { "type": "string" } + } + }, + "project": { + "type": "string", + "description": "Project identifier matching the preset name" + }, + "preset": { + "type": "string", + "description": "URL to the brand preset JSON file" + }, + "brand": { + "type": "object", + "description": "Brand overrides (if different from preset)", + "properties": { + "colors": { + "type": "object", + "properties": { + "primary": { "type": "string" }, + "secondary": { "type": "string" }, + "background": { "type": "string" }, + "foreground": { "type": "string" } + } + }, + "fonts": { + "type": "object", + "properties": { + "heading": { "type": "string" }, + "body": { "type": "string" } + } + } + } + }, + "infrastructure": { + "type": "object", + "required": ["server"], + "properties": { + "server": { + "type": "string", + "description": "Server short name (e.g., slcbride, abundance)" + }, + "server_ip": { + "type": "string", + "format": "ipv4" + }, + "database": { + "type": "string", + "description": "Database identifier" + }, + "coolify_app_id": { + "type": "string", + "description": "Coolify application UUID" + }, + "coolify_url": { + "type": "string", + "format": "uri" + } + } + }, + "seo": { + "type": "object", + "properties": { + "schema_templates": { + "type": "array", + "items": { + "type": "string", + "enum": ["product", "event", "local-business", "faq"] + } + }, + "internal_link_patterns": { + "type": "array", + "items": { "type": "string" } + }, + "title_template": { "type": "string" }, + "default_description": { "type": "string" } + } + }, + "apis": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "base_url": { "type": "string", "format": "uri" }, + "auth_type": { "type": "string", "enum": ["bearer", "api_key", "basic"] }, + "env_key": { "type": "string" } + } + } + }, + "features": { + "type": "object", + "additionalProperties": { "type": "boolean" } + }, + "routes": { + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "additionalProperties": true +} diff --git a/schemas/ai-context.schema.json b/schemas/ai-context.schema.json new file mode 100644 index 0000000..4dea64a --- /dev/null +++ b/schemas/ai-context.schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://git.abundancepartners.app/abundance/ai-ops-templates/schemas/ai-context.schema.json", + "title": "AI Context Manifest", + "description": "Schema for per-project AI context manifest - the single source of truth for AI agents", + "type": "object", + "required": ["client", "infrastructure"], + "properties": { + "_meta": { + "type": "object", + "required": ["last_updated", "version"], + "properties": { + "last_updated": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of last update" + }, + "version": { + "type": "string", + "pattern": "^\\d+\\.\\d+\\.\\d+$", + "description": "Semantic version of context" + }, + "synced_from": { + "type": "string", + "format": "uri", + "description": "URL of the templates repo this was synced from" + }, + "updated_by": { + "type": "string", + "enum": ["ai-agent", "human"], + "description": "Who made the last update" + } + } + }, + "client": { + "type": "string", + "description": "Client/project identifier" + }, + "brand_preset": { + "type": "string", + "format": "uri", + "description": "URL to the brand preset JSON" + }, + "mcp_namespace": { + "type": "string", + "enum": ["all", "ui", "deploy", "seo", "supabase"], + "default": "all", + "description": "Which MCP tools are available for this project" + }, + "infrastructure": { + "type": "object", + "required": ["server"], + "properties": { + "server": { + "type": "string", + "description": "Server short name" + }, + "database": { + "type": "string", + "description": "Database identifier or 'from-env'" + }, + "coolify_app_id": { + "type": "string", + "description": "Coolify app UUID or 'from-env'" + }, + "coolify_staging_app_id": { + "type": "string", + "description": "Staging Coolify app UUID" + } + } + }, + "common_patterns": { + "type": "array", + "items": { "type": "string" }, + "description": "Paths to commonly used snippet files" + }, + "routes": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Route patterns for the application" + }, + "database_schema": { + "type": "object", + "properties": { + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "key_columns": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + } + }, + "api_endpoints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "path": { "type": "string" }, + "method": { "type": "string", "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] }, + "description": { "type": "string" } + } + } + }, + "notes": { + "type": "string", + "description": "Any additional context for AI agents" + } + }, + "additionalProperties": true +} diff --git a/skill-templates/client-skill.template.md b/skill-templates/client-skill.template.md new file mode 100644 index 0000000..a70ffe1 --- /dev/null +++ b/skill-templates/client-skill.template.md @@ -0,0 +1,173 @@ +--- +name: {{CLIENT_NAME}} +description: Context and operations guide for {{CLIENT_NAME}} project. Use when working on {{DESCRIPTION}}. +--- + +# {{CLIENT_NAME}} + +{{PROJECT_DESCRIPTION}} + +## Project Overview + +| Property | Value | +|----------|-------| +| **Client** | {{CLIENT_DISPLAY_NAME}} | +| **Site URL** | {{SITE_URL}} | +| **Server** | {{SERVER_NAME}} ({{SERVER_IP}}) | +| **Database** | {{DATABASE_NAME}} | +| **Status** | {{PROJECT_STATUS}} | + +--- + +## Infrastructure + +### Server Access +```bash +# SSH into server +ssh root@{{SERVER_IP}} + +# Or use abundance CLI +abundance srv ssh {{SERVER_NAME}} +``` + +### Coolify Deployment +- **Dashboard**: {{COOLIFY_URL}} +- **App ID**: {{COOLIFY_APP_ID}} +- **Deploy**: `abundance coolify deploy application {{COOLIFY_APP_ID}}` + +### Database +```bash +# Query database +abundance db query {{DATABASE_NAME}} "SELECT * FROM {{TABLE}} LIMIT 5" + +# Create SSH tunnel +abundance db tunnel {{DATABASE_NAME}} +``` + +--- + +## Brand Guidelines + +### Colors +| Color | Hex | Usage | +|-------|-----|-------| +| Primary | `{{PRIMARY_COLOR}}` | Buttons, links, highlights | +| Secondary | `{{SECONDARY_COLOR}}` | Accents, badges | +| Background | `{{BACKGROUND_COLOR}}` | Page background | +| Text | `{{TEXT_COLOR}}` | Body text | + +### Typography +- **Headings**: {{HEADING_FONT}} +- **Body**: {{BODY_FONT}} + +### Tailwind Config +Brand colors are defined in `presets/{{CLIENT_SLUG}}.json`. Reference via CSS variables: +```css +background-color: var(--primary); +``` + +--- + +## Key Routes + +| Route | Description | Component | +|-------|-------------|-----------| +| {{ROUTE_1}} | {{ROUTE_1_DESC}} | `{{ROUTE_1_COMPONENT}}` | +| {{ROUTE_2}} | {{ROUTE_2_DESC}} | `{{ROUTE_2_COMPONENT}}` | +| {{ROUTE_3}} | {{ROUTE_3_DESC}} | `{{ROUTE_3_COMPONENT}}` | + +--- + +## Database Schema + +### Key Tables +| Table | Description | Key Columns | +|-------|-------------|-------------| +| {{TABLE_1}} | {{TABLE_1_DESC}} | {{TABLE_1_COLS}} | +| {{TABLE_2}} | {{TABLE_2_DESC}} | {{TABLE_2_COLS}} | + +### Common Queries +```sql +-- {{QUERY_1_DESC}} +SELECT * FROM {{TABLE}} WHERE {{CONDITION}}; +``` + +--- + +## APIs + +### {{API_1_NAME}} +- **Base URL**: `{{API_1_URL}}` +- **Auth**: {{API_1_AUTH}} +- **Env Key**: `{{API_1_ENV_KEY}}` + +--- + +## Common Patterns + +### {{PATTERN_1_NAME}} +```{{PATTERN_1_LANG}} +{{PATTERN_1_CODE}} +``` + +### {{PATTERN_2_NAME}} +```{{PATTERN_2_LANG}} +{{PATTERN_2_CODE}} +``` + +--- + +## Integrations + +| Service | Purpose | Config Location | +|---------|---------|-----------------| +| {{SERVICE_1}} | {{SERVICE_1_PURPOSE}} | {{SERVICE_1_CONFIG}} | +| {{SERVICE_2}} | {{SERVICE_2_PURPOSE}} | {{SERVICE_2_CONFIG}} | + +--- + +## Troubleshooting + +### {{ISSUE_1}} +**Symptom**: {{ISSUE_1_SYMPTOM}} +**Solution**: {{ISSUE_1_SOLUTION}} + +### {{ISSUE_2}} +**Symptom**: {{ISSUE_2_SYMPTOM}} +**Solution**: {{ISSUE_2_SOLUTION}} + +--- + +## Recent Changes + +| Date | Change | Notes | +|------|--------|-------| +| {{DATE_1}} | {{CHANGE_1}} | {{NOTES_1}} | + +--- + +## Context Metadata (Auto-Updated) + +| Field | Value | +|-------|-------| +| _last_updated | {{LAST_UPDATED}} | +| _version | {{VERSION}} | +| _synced_from | git.abundancepartners.app/abundance/ai-ops-templates | + +--- + +## Quick Commands + +```bash +# Check server status +abundance srv status + +# View logs +abundance docker logs {{SERVER_NAME}} {{CONTAINER}} -f --tail 100 + +# Deploy to staging +deploy_push({ target: "staging", app_id: "{{STAGING_APP_ID}}" }) + +# Deploy to production +deploy_push({ target: "prod", app_id: "{{PROD_APP_ID}}" }) +``` diff --git a/snippets/add-internal-link.tsx b/snippets/add-internal-link.tsx new file mode 100644 index 0000000..86dc6ad --- /dev/null +++ b/snippets/add-internal-link.tsx @@ -0,0 +1,39 @@ +import Link from 'next/link' +import { cn } from '@/lib/utils' + +interface InternalLinkProps { + href: string + children: React.ReactNode + className?: string + variant?: 'default' | 'muted' | 'underline' +} + +export function InternalLink({ + href, + children, + className, + variant = 'default', +}: InternalLinkProps) { + const variants = { + default: 'text-primary hover:text-primary/80', + muted: 'text-muted-foreground hover:text-foreground', + underline: 'text-foreground underline underline-offset-4 hover:text-primary', + } + + return ( + + {children} + + ) +} + +// Usage example: +// Wedding Photography +// View all venues diff --git a/snippets/alt-text-placeholder.go b/snippets/alt-text-placeholder.go new file mode 100644 index 0000000..c9eac65 --- /dev/null +++ b/snippets/alt-text-placeholder.go @@ -0,0 +1,49 @@ +package templates + +// AltTextPlaceholder generates SEO-friendly alt text for images +// Replace placeholders with actual data before use + +type ImageContext struct { + Subject string // What is shown in the image + Context string // What's happening / setting + Location string // Where the image was taken + Relevance string // Why it's relevant to the page +} + +func GenerateAltText(ctx ImageContext) string { + // Pattern: [Subject] + [Context] + [Location] + [Relevance] + // Example: "Wedding photographer capturing moments at outdoor ceremony in Salt Lake City" + + parts := []string{} + + if ctx.Subject != "" { + parts = append(parts, ctx.Subject) + } + if ctx.Context != "" { + parts = append(parts, ctx.Context) + } + if ctx.Location != "" { + parts = append(parts, "in "+ctx.Location) + } + if ctx.Relevance != "" { + parts = append(parts, ctx.Relevance) + } + + alt := strings.Join(parts, " ") + + // Truncate to 125 characters max (SEO best practice) + if len(alt) > 125 { + alt = alt[:122] + "..." + } + + return alt +} + +// Example usage: +// alt := GenerateAltText(ImageContext{ +// Subject: "Bride and groom", +// Context: "exchanging vows", +// Location: "Salt Lake City Temple", +// Relevance: "wedding ceremony", +// }) +// Result: "Bride and groom exchanging vows in Salt Lake City Temple wedding ceremony" diff --git a/snippets/breadcrumb.tsx b/snippets/breadcrumb.tsx new file mode 100644 index 0000000..8186c29 --- /dev/null +++ b/snippets/breadcrumb.tsx @@ -0,0 +1,78 @@ +import Link from 'next/link' +import { ChevronRight, Home } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface BreadcrumbItem { + label: string + href?: string +} + +interface BreadcrumbProps { + items: BreadcrumbItem[] + className?: string +} + +export function Breadcrumb({ items, className }: BreadcrumbProps) { + // Generate JSON-LD structured data for SEO + const structuredData = { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: items.map((item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.label, + item: item.href ? `${process.env.NEXT_PUBLIC_SITE_URL}${item.href}` : undefined, + })), + } + + return ( + <> + {/* Inject structured data */} +