From 35cf0caed52ac1f2a1a7fa196d052d150086d8bb Mon Sep 17 00:00:00 2001 From: Paul Duvall Date: Sat, 13 Dec 2025 13:46:51 -0500 Subject: [PATCH] feat: simplify centralized rules with sync-based approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace complex gateway pattern with simple Git-based sync strategy that: - Auto-generates CLAUDE.md, AGENTS.md, .cursorrules from central repository - Auto-detects language (Python, TypeScript, Go, etc.) and framework (React, Django, etc.) - Works offline after initial sync (no API calls required) - Compatible with all AI assistants (Claude Code, Cursor, Gemini) - Consolidates documentation into single comprehensive README Changes: - New sync-strategy/ with sync-ai-rules.sh (auto-detection & generation) - Quick-start helper: create-central-repo.sh - Example rules: Python, React, universal security/testing standards - Moved old gateway approach to gateway-strategy/ (advanced use cases) - Updated experiments/README.md pattern description - Consolidated centralized-rules/README.md (removed duplicate docs) Benefits over old approach: - 100 lines bash vs 500+ lines TypeScript - No Node.js service to deploy - Works with standard AI config files - Language/framework-aware rule selection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Sonnet 4.5 --- .../examples/centralized-rules/README.md | 492 ++++++++++++++--- .../gateway-strategy/README-GATEWAY.md | 122 +++++ .../ai-dev-cli/package.json | 0 .../ai-dev-cli/src/cli.ts | 0 .../ai-gateway/package.json | 0 .../ai-gateway/src/claudeClient.ts | 0 .../ai-gateway/src/server.ts | 0 .../org-ai-client/package.json | 0 .../org-ai-client/src/index.ts | 0 .../sync-strategy/create-central-repo.sh | 346 ++++++++++++ .../base/universal-rules.md | 126 +++++ .../example-central-repo/frameworks/react.md | 516 ++++++++++++++++++ .../example-central-repo/languages/python.md | 283 ++++++++++ .../sync-strategy/sync-ai-rules.sh | 294 ++++++++++ 14 files changed, 2103 insertions(+), 76 deletions(-) create mode 100644 experiments/examples/centralized-rules/gateway-strategy/README-GATEWAY.md rename experiments/examples/centralized-rules/{ => gateway-strategy}/ai-dev-cli/package.json (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/ai-dev-cli/src/cli.ts (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/ai-gateway/package.json (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/ai-gateway/src/claudeClient.ts (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/ai-gateway/src/server.ts (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/org-ai-client/package.json (100%) rename experiments/examples/centralized-rules/{ => gateway-strategy}/org-ai-client/src/index.ts (100%) create mode 100755 experiments/examples/centralized-rules/sync-strategy/create-central-repo.sh create mode 100644 experiments/examples/centralized-rules/sync-strategy/example-central-repo/base/universal-rules.md create mode 100644 experiments/examples/centralized-rules/sync-strategy/example-central-repo/frameworks/react.md create mode 100644 experiments/examples/centralized-rules/sync-strategy/example-central-repo/languages/python.md create mode 100755 experiments/examples/centralized-rules/sync-strategy/sync-ai-rules.sh diff --git a/experiments/examples/centralized-rules/README.md b/experiments/examples/centralized-rules/README.md index bb9f84f..63b9c13 100644 --- a/experiments/examples/centralized-rules/README.md +++ b/experiments/examples/centralized-rules/README.md @@ -1,122 +1,462 @@ -# Centralized Rules Example +# Centralized AI Rules -This example demonstrates the three-layer architecture for centralizing AI rules across an organization using the Claude SDK. +Sync organization-wide AI development rules from a central Git repository to your projects. Auto-generates CLAUDE.md, AGENTS.md, and .cursorrules with language/framework-specific rules. -## Architecture +## TL;DR - 30 Second Start +```bash +# 1. Get the quick-start script +./sync-strategy/create-central-repo.sh +# Creates ai-rules-central/ with example rules + +# 2. In your project +curl -O https://raw.githubusercontent.com/yourorg/ai-rules-central/main/sync-ai-rules.sh +chmod +x sync-ai-rules.sh +./sync-ai-rules.sh + +# 3. Done! CLAUDE.md, AGENTS.md, .cursorrules auto-generated +# Claude Code, Cursor, Gemini now follow your org rules +``` + +## How It Works + +``` +┌─────────────────────────────────────────┐ +│ Central Rules Repo (Git) │ +│ github.com/yourorg/ai-rules-central │ +│ │ +│ ├── base/universal-rules.md │ +│ ├── languages/python.md │ +│ └── frameworks/react.md │ +└─────────────────────────────────────────┘ + ↓ [sync-ai-rules.sh] +┌─────────────────────────────────────────┐ +│ Your Project │ +│ │ +│ ├── CLAUDE.md (auto-generated) │ +│ ├── AGENTS.md (auto-generated) │ +│ └── .cursorrules (auto-generated) │ +└─────────────────────────────────────────┘ +``` + +**What happens during sync:** + +1. **Detects your stack** - Finds Python, TypeScript, React, Django, etc. +2. **Fetches relevant rules** - Pulls universal + language + framework rules +3. **Generates config files** - Creates CLAUDE.md, .cursorrules, etc. +4. **Your AI assistant reads them** - Claude Code, Cursor, Gemini automatically follow the rules + +**Key benefits:** +- ✅ **Works with all AI tools** - Claude Code, Cursor, Gemini (uses standard config files) +- ✅ **Offline-friendly** - No API calls, works without internet after sync +- ✅ **Auto-detection** - Finds your language/framework automatically +- ✅ **Simple** - One bash script, no services to deploy +- ✅ **Git-based** - Version-controlled, auditable changes + +## Complete Setup Guide + +### Step 1: Create Central Rules Repository (One-Time Org Setup) + +**Quick way - Use the generator:** +```bash +cd sync-strategy/ +./create-central-repo.sh +# Follow prompts to create ai-rules-central/ ``` -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ ai-dev-cli │ ──► │ ai-gateway │ ──► │ Claude API │ -└─────────────┘ └─────────────┘ └─────────────┘ - │ │ - │ ┌──────┴──────┐ - │ │ Org Rules │ - │ │ Logging │ - │ │ Filters │ - │ └─────────────┘ - │ - └──────────► @yourorg/org-ai-client (alternative) + +**Manual way:** +```bash +gh repo create yourorg/ai-rules-central --private +cd ai-rules-central +mkdir -p base languages frameworks ``` -## Components +Create **base/universal-rules.md** (applies to all projects): +```markdown +# Universal Organization Rules -### 1. ai-gateway/ +## Security +- ❌ NEVER commit secrets, API keys, or credentials +- ✅ Use environment variables for configuration +- ✅ Run `gitleaks detect --no-git` before committing -Internal service that owns all org rules and calls the Claude API. +## Code Quality +- Write tests FIRST (Test-Driven Development) +- Minimum code coverage: 80% +- Follow existing patterns in the codebase -**Features**: -- Centralized system prompts with org rules -- Request/response logging -- Input/output filtering hooks +## Git Workflow +- Use Conventional Commits: `feat:`, `fix:`, `docs:` +- Reference issue numbers: `feat: add logout (#123)` +- Always run tests before pushing +``` -### 2. org-ai-client/ +Create **languages/python.md**: +```markdown +# Python Development Rules -Shared SDK wrapper that embeds org rules. Use this if you don't want a network service yet. +## Code Style +- **Black** for formatting (line length: 88) +- **Type hints** required for all functions +- **Ruff** for linting -**Features**: -- Org rules baked into system prompts -- Consistent interface across all repos -- Version-controlled rule updates +## Testing +- **pytest** for all tests +- **pytest-cov** for coverage (minimum 80%) +- Fixtures in `tests/conftest.py` -### 3. ai-dev-cli/ +## Security +- Pydantic models for all API inputs +- Parameterized queries only (prevent SQL injection) +``` -Developer CLI that calls the gateway or wrapper library. +Create **frameworks/react.md**: +```markdown +# React Development Rules -**Features**: -- Simple commands: `ai-dev plan`, `ai-dev refactor` -- Auto-detects repo context -- Consistent UX across tools +## Component Structure +- ✅ Functional components with hooks only +- ❌ No class components +- ✅ TypeScript required +- ✅ One component per file -## Setup +## State Management +- Local state: `useState` +- Global state: Context API +- Server state: SWR or React Query -### Prerequisites +## Testing +- React Testing Library (test user behavior, not implementation) +- Minimum 80% coverage +``` -- Node.js 18+ -- `ANTHROPIC_API_KEY` environment variable +See [sync-strategy/example-central-repo/](sync-strategy/example-central-repo/) for complete examples. -### Install dependencies +### Step 2: Add Sync Script to Your Projects ```bash -cd ai-gateway && npm install -cd ../org-ai-client && npm install -cd ../ai-dev-cli && npm install +# In your project repository +curl -O https://raw.githubusercontent.com/yourorg/ai-rules-central/main/sync-ai-rules.sh +chmod +x sync-ai-rules.sh + +# Optional: Configure sync behavior +mkdir -p .ai +cat > .ai/sync-config.json << 'EOF' +{ + "centralRepo": "git@github.com:yourorg/ai-rules-central.git", + "rules": { + "base": ["universal-rules"], + "languages": "auto-detect", + "frameworks": "auto-detect" + }, + "outputFormats": ["CLAUDE.md", "AGENTS.md", ".cursorrules"] +} +EOF ``` -### Run the gateway +### Step 3: Run Sync ```bash -cd ai-gateway -npm start -# Gateway running on http://localhost:3000 +./sync-ai-rules.sh + +# Output: +# [sync-ai-rules] Starting AI rules synchronization... +# [sync-ai-rules] Detected languages: python +# [sync-ai-rules] Detected frameworks: fastapi +# [sync-ai-rules] Adding base rules... +# [sync-ai-rules] Adding python rules... +# [sync-ai-rules] Adding fastapi rules... +# [sync-ai-rules] Generating CLAUDE.md... +# [sync-ai-rules] Generating AGENTS.md... +# [sync-ai-rules] Generating .cursorrules... +# [sync-ai-rules] ✅ Sync complete! +``` + +**Generated CLAUDE.md:** +```markdown +# AI Development Rules +# Auto-generated - DO NOT EDIT MANUALLY +# Last synced: 2025-12-13 10:30 UTC +# Rules version: abc123 +# Source: github.com/yourorg/ai-rules-central + +## Universal Organization Rules +[... your universal rules ...] + +## Python Development Rules +[... your Python rules ...] + +## FastAPI Development Rules +[... your FastAPI rules ...] + +--- +To update: ./sync-ai-rules.sh +``` + +### Step 4: AI Assistants Automatically Follow Rules + +- **Claude Code** reads `CLAUDE.md` +- **Cursor IDE** reads `.cursorrules` +- **Gemini/other tools** read `AGENTS.md` + +No configuration needed - they just work! + +## Language & Framework Detection + +The sync script auto-detects your stack: + +| Language | Detection Files | +|----------|----------------| +| **Python** | `pyproject.toml`, `requirements.txt`, `setup.py`, `*.py` | +| **TypeScript** | `tsconfig.json`, `package.json` with `@types/*` | +| **JavaScript** | `package.json` (without TypeScript) | +| **Go** | `go.mod`, `go.sum` | +| **Rust** | `Cargo.toml` | +| **Java** | `pom.xml`, `build.gradle` | +| **Ruby** | `Gemfile`, `*.gemspec` | + +| Framework | Detection Files | +|-----------|----------------| +| **React** | `package.json` with `"react"` dependency | +| **Vue** | `package.json` with `"vue"` dependency | +| **Next.js** | `package.json` with `"next"` dependency | +| **Django** | `manage.py`, `"django"` in dependencies | +| **FastAPI** | `"fastapi"` in `pyproject.toml` or `requirements.txt` | +| **Flask** | `"flask"` in dependencies | +| **Express** | `package.json` with `"express"` dependency | + +**Override auto-detection:** +```json +// .ai/sync-config.json +{ + "languages": ["python", "typescript"], + "frameworks": ["react", "fastapi"], + "excludeRules": ["frameworks/vue"] +} ``` -### Use the CLI +## Keeping Rules Updated +### Option A: Manual Sync ```bash -cd ai-dev-cli -npm link -ai-dev plan "Implement idempotent refund API" +./sync-ai-rules.sh # Run when you need latest rules ``` -## Customization +### Option B: Pre-commit Hook (Recommended) +Auto-sync before every commit: +```bash +cat > .git/hooks/pre-commit << 'EOF' +#!/bin/bash +./sync-ai-rules.sh --quiet +git add CLAUDE.md AGENTS.md .cursorrules 2>/dev/null || true +EOF +chmod +x .git/hooks/pre-commit +``` -### Modify org rules +### Option C: CI/CD Check +Ensure rules are current in pull requests: +```yaml +# .github/workflows/check-rules.yml +name: Check AI Rules Current +on: [pull_request] +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./sync-ai-rules.sh --check-only + # Fails if local rules are outdated +``` + +## Central Repository Organization -Edit the system prompt in `ai-gateway/src/claudeClient.ts` or `org-ai-client/src/index.ts`: +Recommended structure for `ai-rules-central`: -```typescript -const systemPrompt = ` -You are the org AI pair programmer. -Follow ORG RULES v1.4: -- Use spec-first development. -- Your custom rules here... -`; +``` +ai-rules-central/ +├── base/ +│ ├── universal-rules.md # All projects +│ ├── security-baseline.md # Security standards +│ └── git-workflow.md # Commit standards +├── languages/ +│ ├── python.md +│ ├── typescript.md +│ ├── go.md +│ ├── rust.md +│ └── java.md +├── frameworks/ +│ ├── react.md +│ ├── vue.md +│ ├── django.md +│ ├── fastapi.md +│ └── express.md +├── domains/ # Optional: Industry-specific +│ ├── fintech.md +│ ├── healthcare.md +│ └── ecommerce.md +├── compliance/ # Optional: Compliance frameworks +│ ├── pci-dss.md +│ ├── hipaa.md +│ └── soc2.md +└── sync-ai-rules.sh ``` -### Add input/output filters +## Troubleshooting -Extend the gateway with validation: +**Rules not updating?** +```bash +# Force refresh (deletes cache) +./sync-ai-rules.sh --force + +# Check current version +cat .ai/.rules-version +``` -```typescript -// Before calling Claude -if (containsSecrets(input)) { - throw new Error("Input contains secrets"); +**Wrong language/framework detected?** +```bash +# Override in config +cat > .ai/sync-config.json << 'EOF' +{ + "languages": ["python"], + "frameworks": ["django"], + "excludeRules": ["frameworks/react"] } +EOF +./sync-ai-rules.sh +``` + +**Want project-specific customizations?** +```bash +# Generate base rules, then append custom rules +./sync-ai-rules.sh -// After receiving response -if (containsBannedPatterns(response)) { - throw new Error("Response violates policy"); +cat >> CLAUDE.md << 'EOF' + +## Project-Specific Rules + +- Use our custom logging: `from myapp.logger import log` +- Database migrations: `alembic revision --autogenerate` +- API base URL: `https://api.example.com/v1` +EOF +``` + +**Sync script not found?** +```bash +# Download from central repo +curl -O https://raw.githubusercontent.com/yourorg/ai-rules-central/main/sync-ai-rules.sh +chmod +x sync-ai-rules.sh +``` + +## Advanced Features + +### Multi-Environment Rule Sets + +Different rules for production vs prototyping: + +```json +// .ai/sync-config.json +{ + "centralRepo": "git@github.com:yourorg/ai-rules-central.git", + "ruleSet": "production-critical", // or "prototyping" + "rules": { + "base": ["universal-rules", "production-baseline"], + "languages": "auto-detect" + } } ``` -### Integrate policy-as-code +``` +ai-rules-central/ +├── rulesets/ +│ ├── production-critical/ +│ │ └── extra-validation.md # Strict rules +│ └── prototyping/ +│ └── relaxed-standards.md # Faster iteration +``` -Add OPA or Cedar policy checks: +### Custom Rule Categories -```typescript -const allowed = await opa.evaluate("ai/task_allowed", { - repo: input.repo, - taskType: input.taskType, - user: req.user -}); +```json +{ + "customRules": [ + "compliance/hipaa", // Healthcare compliance + "domains/fintech", // Financial services rules + "internal/api-gateway" // Internal tool rules + ] +} ``` + +## Alternative: Gateway Strategy (Advanced) + +For organizations needing **policy enforcement**, **input/output filtering**, or **usage logging**, see [gateway-strategy/](gateway-strategy/) for an API gateway approach. + +**When to use Gateway instead:** +- ❌ Block secrets from being sent to AI providers +- ❌ Scan AI outputs for banned APIs or license violations +- ❌ Enforce policy-as-code (OPA, Cedar) before operations +- ❌ Centralized audit logging for compliance (SOC 2, HIPAA) +- ❌ Usage metrics aggregation across teams + +**Trade-offs:** +- ✅ Enforceable guardrails (not just suggestions) +- ❌ Complex infrastructure (Node.js API service) +- ❌ Doesn't work with Claude Code, Cursor (requires custom CLI) +- ❌ Network dependency (API must be available) + +**For most organizations**: Start with sync strategy (this approach). Add gateway later for specific high-security projects if needed. + +## Quick Comparison + +| Feature | Sync Strategy ⭐ | Gateway Strategy | +|---------|-----------------|------------------| +| **Setup Time** | 5 minutes | 1-2 hours | +| **Infrastructure** | None (Git only) | Node.js API service | +| **AI Tool Support** | All (Claude, Cursor, Gemini) | Custom CLI only | +| **Offline Support** | ✅ Yes | ❌ No | +| **Language Detection** | ✅ Automatic | ❌ Manual | +| **Policy Enforcement** | ❌ Suggestions only | ✅ Enforceable | +| **Input/Output Filtering** | ❌ No | ✅ Yes | +| **Usage Logging** | ❌ No | ✅ Yes | +| **Best For** | Most teams | Enterprises, regulated industries | + +## Examples & Templates + +- **[sync-ai-rules.sh](sync-strategy/sync-ai-rules.sh)** - Main sync script +- **[create-central-repo.sh](sync-strategy/create-central-repo.sh)** - Quick-start generator +- **[example-central-repo/](sync-strategy/example-central-repo/)** - Sample organization rules + - [Universal rules](sync-strategy/example-central-repo/base/universal-rules.md) + - [Python rules](sync-strategy/example-central-repo/languages/python.md) + - [React rules](sync-strategy/example-central-repo/frameworks/react.md) + +## Next Steps + +1. **Create central repository**: Run `./sync-strategy/create-central-repo.sh` +2. **Customize rules**: Edit markdown files in `ai-rules-central/` +3. **Push to GitHub**: `gh repo create yourorg/ai-rules-central --private --source=. --push` +4. **Sync to projects**: Add sync script to each project repository +5. **Automate**: Add pre-commit hook or CI check + +## Questions? + +**Which strategy should I use?** +- Start with Sync Strategy unless you specifically need policy enforcement or input/output filtering +- You can always add Gateway later for specific high-security repos + +**Does this work with my AI tool?** +- ✅ Claude Code (reads `CLAUDE.md`) +- ✅ Cursor IDE (reads `.cursorrules`) +- ✅ Gemini CLI (reads `AGENTS.md`) +- ✅ Any tool reading standard config files + +**How often should I sync?** +- Use pre-commit hook for automatic updates +- Or manually sync when you need latest rule changes + +**Can I have project-specific rules?** +- Yes! Generate base rules with sync, then append custom rules to the generated files +- Or override languages/frameworks in `.ai/sync-config.json` + +**How do I update organization rules?** +- Edit markdown files in `ai-rules-central` repository +- Create PR, get approval, merge +- Projects sync automatically (if using pre-commit hook) or manually diff --git a/experiments/examples/centralized-rules/gateway-strategy/README-GATEWAY.md b/experiments/examples/centralized-rules/gateway-strategy/README-GATEWAY.md new file mode 100644 index 0000000..bb9f84f --- /dev/null +++ b/experiments/examples/centralized-rules/gateway-strategy/README-GATEWAY.md @@ -0,0 +1,122 @@ +# Centralized Rules Example + +This example demonstrates the three-layer architecture for centralizing AI rules across an organization using the Claude SDK. + +## Architecture + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ ai-dev-cli │ ──► │ ai-gateway │ ──► │ Claude API │ +└─────────────┘ └─────────────┘ └─────────────┘ + │ │ + │ ┌──────┴──────┐ + │ │ Org Rules │ + │ │ Logging │ + │ │ Filters │ + │ └─────────────┘ + │ + └──────────► @yourorg/org-ai-client (alternative) +``` + +## Components + +### 1. ai-gateway/ + +Internal service that owns all org rules and calls the Claude API. + +**Features**: +- Centralized system prompts with org rules +- Request/response logging +- Input/output filtering hooks + +### 2. org-ai-client/ + +Shared SDK wrapper that embeds org rules. Use this if you don't want a network service yet. + +**Features**: +- Org rules baked into system prompts +- Consistent interface across all repos +- Version-controlled rule updates + +### 3. ai-dev-cli/ + +Developer CLI that calls the gateway or wrapper library. + +**Features**: +- Simple commands: `ai-dev plan`, `ai-dev refactor` +- Auto-detects repo context +- Consistent UX across tools + +## Setup + +### Prerequisites + +- Node.js 18+ +- `ANTHROPIC_API_KEY` environment variable + +### Install dependencies + +```bash +cd ai-gateway && npm install +cd ../org-ai-client && npm install +cd ../ai-dev-cli && npm install +``` + +### Run the gateway + +```bash +cd ai-gateway +npm start +# Gateway running on http://localhost:3000 +``` + +### Use the CLI + +```bash +cd ai-dev-cli +npm link +ai-dev plan "Implement idempotent refund API" +``` + +## Customization + +### Modify org rules + +Edit the system prompt in `ai-gateway/src/claudeClient.ts` or `org-ai-client/src/index.ts`: + +```typescript +const systemPrompt = ` +You are the org AI pair programmer. +Follow ORG RULES v1.4: +- Use spec-first development. +- Your custom rules here... +`; +``` + +### Add input/output filters + +Extend the gateway with validation: + +```typescript +// Before calling Claude +if (containsSecrets(input)) { + throw new Error("Input contains secrets"); +} + +// After receiving response +if (containsBannedPatterns(response)) { + throw new Error("Response violates policy"); +} +``` + +### Integrate policy-as-code + +Add OPA or Cedar policy checks: + +```typescript +const allowed = await opa.evaluate("ai/task_allowed", { + repo: input.repo, + taskType: input.taskType, + user: req.user +}); +``` diff --git a/experiments/examples/centralized-rules/ai-dev-cli/package.json b/experiments/examples/centralized-rules/gateway-strategy/ai-dev-cli/package.json similarity index 100% rename from experiments/examples/centralized-rules/ai-dev-cli/package.json rename to experiments/examples/centralized-rules/gateway-strategy/ai-dev-cli/package.json diff --git a/experiments/examples/centralized-rules/ai-dev-cli/src/cli.ts b/experiments/examples/centralized-rules/gateway-strategy/ai-dev-cli/src/cli.ts similarity index 100% rename from experiments/examples/centralized-rules/ai-dev-cli/src/cli.ts rename to experiments/examples/centralized-rules/gateway-strategy/ai-dev-cli/src/cli.ts diff --git a/experiments/examples/centralized-rules/ai-gateway/package.json b/experiments/examples/centralized-rules/gateway-strategy/ai-gateway/package.json similarity index 100% rename from experiments/examples/centralized-rules/ai-gateway/package.json rename to experiments/examples/centralized-rules/gateway-strategy/ai-gateway/package.json diff --git a/experiments/examples/centralized-rules/ai-gateway/src/claudeClient.ts b/experiments/examples/centralized-rules/gateway-strategy/ai-gateway/src/claudeClient.ts similarity index 100% rename from experiments/examples/centralized-rules/ai-gateway/src/claudeClient.ts rename to experiments/examples/centralized-rules/gateway-strategy/ai-gateway/src/claudeClient.ts diff --git a/experiments/examples/centralized-rules/ai-gateway/src/server.ts b/experiments/examples/centralized-rules/gateway-strategy/ai-gateway/src/server.ts similarity index 100% rename from experiments/examples/centralized-rules/ai-gateway/src/server.ts rename to experiments/examples/centralized-rules/gateway-strategy/ai-gateway/src/server.ts diff --git a/experiments/examples/centralized-rules/org-ai-client/package.json b/experiments/examples/centralized-rules/gateway-strategy/org-ai-client/package.json similarity index 100% rename from experiments/examples/centralized-rules/org-ai-client/package.json rename to experiments/examples/centralized-rules/gateway-strategy/org-ai-client/package.json diff --git a/experiments/examples/centralized-rules/org-ai-client/src/index.ts b/experiments/examples/centralized-rules/gateway-strategy/org-ai-client/src/index.ts similarity index 100% rename from experiments/examples/centralized-rules/org-ai-client/src/index.ts rename to experiments/examples/centralized-rules/gateway-strategy/org-ai-client/src/index.ts diff --git a/experiments/examples/centralized-rules/sync-strategy/create-central-repo.sh b/experiments/examples/centralized-rules/sync-strategy/create-central-repo.sh new file mode 100755 index 0000000..8a5ddaa --- /dev/null +++ b/experiments/examples/centralized-rules/sync-strategy/create-central-repo.sh @@ -0,0 +1,346 @@ +#!/bin/bash +# create-central-repo.sh +# Quick-start script to create a central AI rules repository for your organization + +set -e + +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${BLUE}=== AI Rules Central Repository Setup ===${NC}\n" + +# Get organization name +read -p "Organization name (e.g., yourorg): " ORG_NAME +ORG_NAME=${ORG_NAME:-yourorg} + +# Get repository name +REPO_NAME="ai-rules-central" +read -p "Repository name [${REPO_NAME}]: " INPUT_REPO +REPO_NAME=${INPUT_REPO:-$REPO_NAME} + +# Create directory structure +echo -e "\n${GREEN}Creating directory structure...${NC}" +mkdir -p "${REPO_NAME}" +cd "${REPO_NAME}" + +mkdir -p base +mkdir -p languages +mkdir -p frameworks +mkdir -p domains +mkdir -p compliance + +# Create base universal rules +echo -e "${GREEN}Creating base/universal-rules.md...${NC}" +cat > base/universal-rules.md << 'EOF' +# Universal Organization Rules + +These rules apply to ALL projects regardless of language or framework. + +## Security Standards + +### Secret Management +- ❌ NEVER commit secrets, API keys, passwords, or credentials +- ✅ Use environment variables for all configuration +- ✅ Run secret scanning before commits: `gitleaks detect --no-git` + +### Security Scanning +Before any commit: +```bash +# Scan for hardcoded secrets +gitleaks detect --no-git --source=. +``` + +## Code Quality + +### Testing Standards +- Write tests FIRST (Test-Driven Development) +- Minimum code coverage: **80%** +- Test edge cases and error conditions + +### Code Style +- Follow existing patterns in the codebase +- Self-documenting code with clear variable/function names +- Comments explain **WHY**, not WHAT + +### Error Handling +- Handle ALL errors explicitly +- Log errors with context (request ID, user ID, operation) +- Return meaningful error messages to users + +## Git Workflow + +### Commit Standards +- Use Conventional Commits format: + - `feat: add user authentication` + - `fix: prevent duplicate orders` + - `docs: update API documentation` +- Reference issue numbers: `feat: add logout (#123)` + +### Before Pushing +Always run: +```bash +# 1. Run tests +npm test # or pytest, go test, etc. + +# 2. Check git diff +git diff --cached + +# 3. Verify no secrets +gitleaks detect --no-git +``` + +## AI Development Standards + +### Specification-Driven +- Write specifications BEFORE implementation +- Use formal specs: OpenAPI, JSON Schema, Gherkin +- Link code to specification IDs for traceability + +### Communication +- Ask clarifying questions before major changes +- Explain non-obvious implementation decisions +- Propose alternatives when requirements are ambiguous + +--- + +**Questions about these rules?** Contact: dev-leads@${ORG_NAME}.com +**Rule updates:** Create PR in `${REPO_NAME}` repository +EOF + +# Create Python rules +echo -e "${GREEN}Creating languages/python.md...${NC}" +cat > languages/python.md << 'EOF' +# Python Development Rules + +## Code Style +- **Tool**: Black (line length: 88 characters) +- **Import sorting**: isort with Black-compatible settings +- **Linting**: Ruff (replaces flake8, pylint) + +### Type Hints +Required for all functions (parameters and return types): +```python +def process_payment(user_id: int, amount: Decimal) -> PaymentResult: + ... +``` + +## Testing +- **Framework**: pytest +- **Coverage**: pytest-cov (minimum 80%) +- **Fixtures**: Define in `tests/conftest.py` + +## Security +- Use Pydantic models for all API inputs +- Never trust user input +- Parameterized queries only (prevent SQL injection) + +## Tools +```bash +# Format code +black . && isort . + +# Type check +mypy src/ + +# Test +pytest -v --cov=src +``` +EOF + +# Create TypeScript rules +echo -e "${GREEN}Creating languages/typescript.md...${NC}" +cat > languages/typescript.md << 'EOF' +# TypeScript Development Rules + +## Code Style +- **Strict mode**: Always enabled in `tsconfig.json` +- **Linting**: ESLint with recommended rules +- **Formatting**: Prettier (line length: 100) + +## Type Safety +- ✅ Type all function parameters and return values +- ❌ Never use `any` (use `unknown` if type is truly unknown) +- ✅ Use strict null checks + +```typescript +// CORRECT +function getUser(id: number): Promise { + ... +} + +// WRONG - using any +function getUser(id: any): any { + ... +} +``` + +## Testing +- **Framework**: Jest or Vitest +- **Coverage**: Minimum 80% +- **Mocking**: Use jest.mock() or vi.mock() + +## Tools +```bash +# Type check +tsc --noEmit + +# Lint +eslint src/ + +# Test +npm test +``` +EOF + +# Create React rules +echo -e "${GREEN}Creating frameworks/react.md...${NC}" +cat > frameworks/react.md << 'EOF' +# React Development Rules + +## Component Structure +- ✅ Always use functional components with hooks +- ❌ No class components +- ✅ One component per file +- ✅ TypeScript required + +## Hooks +- Always call hooks at top level +- Use ESLint plugin: `eslint-plugin-react-hooks` +- Extract reusable logic into custom hooks + +## State Management +- **Local state**: useState for component-specific state +- **Global state**: Context API (avoid prop drilling >2 levels) +- **Server state**: SWR or React Query + +## Testing +- **Framework**: React Testing Library +- **Focus**: Test user behavior, not implementation +- **Accessibility**: Test with screen readers in mind + +```tsx +// CORRECT - Testing user behavior +expect(screen.getByRole('button', { name: /submit/i })).toBeInTheDocument(); + +// WRONG - Testing implementation +expect(component.state.isOpen).toBe(true); +``` +EOF + +# Create Django rules +echo -e "${GREEN}Creating frameworks/django.md...${NC}" +cat > frameworks/django.md << 'EOF' +# Django Development Rules + +## Project Structure +- Use Django's recommended layout +- Settings split by environment (base, dev, prod) +- Migrations tracked in version control + +## Models +- Always add `__str__()` method +- Use `verbose_name` and `help_text` for fields +- Add database indexes for frequently queried fields + +## Security +- NEVER disable CSRF protection +- Use Django's built-in auth system +- Validate all user input with forms/serializers + +## Testing +- Use Django's TestCase for database tests +- Use pytest-django for complex scenarios +- Factory Boy for test data generation + +```python +# CORRECT - Using Django TestCase +from django.test import TestCase + +class UserModelTest(TestCase): + def test_create_user(self): + user = User.objects.create(username="test") + self.assertEqual(user.username, "test") +``` +EOF + +# Create README +echo -e "${GREEN}Creating README.md...${NC}" +cat > README.md << EOF +# ${ORG_NAME} AI Rules Central Repository + +Central repository for organization-wide AI development rules. + +## Structure + +\`\`\` +${REPO_NAME}/ +├── base/ # Universal rules for all projects +├── languages/ # Language-specific rules +├── frameworks/ # Framework-specific rules +├── domains/ # Domain-specific rules (fintech, healthcare) +└── compliance/ # Compliance frameworks (PCI-DSS, HIPAA) +\`\`\` + +## Usage + +In your project repository: + +\`\`\`bash +# Download sync script +curl -O https://raw.githubusercontent.com/${ORG_NAME}/${REPO_NAME}/main/sync-ai-rules.sh +chmod +x sync-ai-rules.sh + +# Run sync +./sync-ai-rules.sh + +# Generates CLAUDE.md, AGENTS.md, .cursorrules with relevant rules +\`\`\` + +## Adding New Rules + +1. Create PR with new rule file (e.g., \`languages/go.md\`) +2. Follow existing format (see \`base/universal-rules.md\`) +3. Get approval from tech leads +4. Merge → automatically available to all projects on next sync + +## Updating Rules + +1. Edit rule files in this repository +2. Create PR with changes +3. After merge, projects sync with \`./sync-ai-rules.sh\` + +## Questions? + +Contact: dev-leads@${ORG_NAME}.com +EOF + +# Copy sync script +echo -e "${GREEN}Copying sync-ai-rules.sh...${NC}" +cp ../sync-ai-rules.sh . + +# Initialize git +echo -e "${GREEN}Initializing git repository...${NC}" +git init +git add . +git commit -m "Initial commit: AI rules central repository" + +# Display next steps +echo -e "\n${GREEN}✅ Central repository created successfully!${NC}\n" +echo -e "${YELLOW}Next steps:${NC}" +echo -e "1. Review and customize rules in ${BLUE}${REPO_NAME}/${NC}" +echo -e " - base/universal-rules.md" +echo -e " - languages/*.md" +echo -e " - frameworks/*.md" +echo "" +echo -e "2. Create remote repository:" +echo -e " ${BLUE}cd ${REPO_NAME}${NC}" +echo -e " ${BLUE}gh repo create ${ORG_NAME}/${REPO_NAME} --private --source=. --push${NC}" +echo "" +echo -e "3. In your project repositories:" +echo -e " ${BLUE}curl -O https://raw.githubusercontent.com/${ORG_NAME}/${REPO_NAME}/main/sync-ai-rules.sh${NC}" +echo -e " ${BLUE}chmod +x sync-ai-rules.sh${NC}" +echo -e " ${BLUE}./sync-ai-rules.sh${NC}" +echo "" +echo -e "${GREEN}Done! Your organization now has centralized AI rules.${NC}" diff --git a/experiments/examples/centralized-rules/sync-strategy/example-central-repo/base/universal-rules.md b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/base/universal-rules.md new file mode 100644 index 0000000..68ade5a --- /dev/null +++ b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/base/universal-rules.md @@ -0,0 +1,126 @@ +# Universal Organization Rules + +These rules apply to ALL projects regardless of language or framework. + +## Security Standards + +### Secret Management +- ❌ NEVER commit secrets, API keys, passwords, or credentials +- ✅ Use environment variables for all configuration +- ✅ Run secret scanning before commits: `gitleaks detect --no-git` +- ✅ Use secret management services (AWS Secrets Manager, 1Password) + +### Sensitive Files +Block editing of these files: +- `.env`, `.env.*` +- `credentials.json`, `secrets.yaml` +- `**/config/production.yml` +- Any file containing API keys or tokens + +### Security Scanning +Before any commit: +```bash +# Scan for hardcoded secrets +gitleaks detect --no-git --source=. + +# Check dependencies for vulnerabilities +# Python: pip-audit or safety +# Node.js: npm audit +# Go: govulncheck +``` + +## Code Quality + +### Testing Standards +- Write tests FIRST (Test-Driven Development) +- Minimum code coverage: **80%** +- Test edge cases and error conditions +- Use meaningful test names that describe behavior + +### Code Style +- Follow existing patterns in the codebase +- Self-documenting code with clear variable/function names +- Comments explain **WHY**, not WHAT +- Keep functions small (<50 lines) and single-purpose + +### Error Handling +- Handle ALL errors explicitly +- Never use bare `except:` or `catch (e) {}` +- Log errors with context (request ID, user ID, operation) +- Return meaningful error messages to users + +## Git Workflow + +### Commit Standards +- Use Conventional Commits format: + - `feat: add user authentication` + - `fix: prevent duplicate orders` + - `docs: update API documentation` + - `refactor: simplify payment logic` +- Reference issue numbers: `feat: add logout (#123)` +- Keep commits atomic (one logical change per commit) + +### Branch Naming +- `feature/short-description` +- `fix/bug-description` +- `refactor/area-being-refactored` + +### Before Pushing +Always run: +```bash +# 1. Run tests +npm test # or pytest, go test, etc. + +# 2. Check git diff +git diff --cached + +# 3. Verify no secrets +gitleaks detect --no-git +``` + +## AI Development Standards + +### Specification-Driven +- Write specifications BEFORE implementation +- Use formal specs: OpenAPI, JSON Schema, Gherkin +- Link code to specification IDs for traceability + +### Progressive Disclosure +When working in specialized areas, load additional rules: +- **API work**: Load API design rules +- **Security changes**: Load security-specific rules +- **Database migrations**: Load database rules + +### Communication +- Ask clarifying questions before major changes +- Explain non-obvious implementation decisions +- Propose alternatives when requirements are ambiguous + +## Compliance + +### Data Protection +- Never log PII (emails, names, addresses) +- Redact sensitive data in error messages +- Follow GDPR/CCPA data handling requirements + +### Audit Trail +- Log significant actions (authentication, data changes) +- Include: timestamp, user ID, action, result +- Retention: 90 days minimum for audit logs + +## Development Environment + +### Documentation +- Update README.md when adding features +- Document environment variables in `.env.example` +- Keep API documentation current (OpenAPI/Swagger) + +### Dependencies +- Pin versions in production (`package-lock.json`, `requirements.txt`) +- Review security advisories before upgrading +- Test upgrades in staging before production + +--- + +**Questions about these rules?** Contact: dev-leads@yourorg.com +**Rule updates:** Create PR in `ai-rules-central` repository diff --git a/experiments/examples/centralized-rules/sync-strategy/example-central-repo/frameworks/react.md b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/frameworks/react.md new file mode 100644 index 0000000..6adae3f --- /dev/null +++ b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/frameworks/react.md @@ -0,0 +1,516 @@ +# React Development Rules + +Framework-specific rules for React projects. + +## Component Structure + +### Functional Components +- ✅ Always use functional components with hooks +- ❌ No class components (legacy) +- ✅ One component per file +- ✅ Named exports for components + +```tsx +// CORRECT - Functional component with hooks +export function UserProfile({ userId }: { userId: number }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchUser(userId).then(setUser).finally(() => setLoading(false)); + }, [userId]); + + if (loading) return ; + if (!user) return ; + + return
...
; +} + +// WRONG - Class component (legacy) +class UserProfile extends React.Component { + ... +} +``` + +### File Organization +``` +src/ +├── components/ +│ ├── UserProfile/ +│ │ ├── UserProfile.tsx # Component +│ │ ├── UserProfile.test.tsx # Tests +│ │ ├── UserProfile.module.css # Styles +│ │ └── index.ts # Re-export +│ └── shared/ +│ ├── Button/ +│ ├── Input/ +│ └── Modal/ +├── hooks/ +│ ├── useAuth.ts +│ ├── useApi.ts +│ └── useForm.ts +├── pages/ +│ ├── Home.tsx +│ └── Dashboard.tsx +└── utils/ + ├── api.ts + └── validation.ts +``` + +## Type Safety + +### TypeScript +- ✅ Required for all React projects +- ✅ Strict mode enabled in `tsconfig.json` +- ✅ Type all props, state, and hooks + +```tsx +// Component props interface +interface UserCardProps { + user: User; + onEdit: (userId: number) => void; + isAdmin?: boolean; // Optional prop +} + +export function UserCard({ user, onEdit, isAdmin = false }: UserCardProps) { + return ( +
+

{user.name}

+ {isAdmin && } +
+ ); +} +``` + +### Avoid `any` +```tsx +// WRONG - Using any +function SearchResults({ data }: { data: any }) { + return data.map((item: any) =>
{item.name}
); +} + +// CORRECT - Proper typing +interface SearchItem { + id: number; + name: string; + description: string; +} + +function SearchResults({ data }: { data: SearchItem[] }) { + return data.map(item =>
{item.name}
); +} +``` + +## State Management + +### Local State (useState) +Use for component-specific state: +```tsx +function Counter() { + const [count, setCount] = useState(0); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +### Global State (Context API) +Use for app-wide state (avoid prop drilling >2 levels): +```tsx +// contexts/AuthContext.tsx +interface AuthContextType { + user: User | null; + login: (credentials: Credentials) => Promise; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = useState(null); + + const login = async (credentials: Credentials) => { + const user = await authApi.login(credentials); + setUser(user); + }; + + const logout = () => { + setUser(null); + }; + + return ( + + {children} + + ); +} + +export function useAuth() { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within AuthProvider'); + } + return context; +} +``` + +### When to Use Redux/Zustand +- Complex state with many interdependencies +- Need for time-travel debugging +- Large team needing strict state patterns + +For most projects, Context API is sufficient. + +## Hooks + +### Custom Hooks +Extract reusable logic into custom hooks: +```tsx +// hooks/useApi.ts +function useApi(url: string) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + + fetch(url) + .then(res => res.json()) + .then(data => { + if (!cancelled) { + setData(data); + setLoading(false); + } + }) + .catch(err => { + if (!cancelled) { + setError(err); + setLoading(false); + } + }); + + return () => { + cancelled = true; // Cleanup on unmount + }; + }, [url]); + + return { data, loading, error }; +} + +// Usage +function UserList() { + const { data, loading, error } = useApi('/api/users'); + + if (loading) return ; + if (error) return ; + + return
    {data?.map(user =>
  • {user.name}
  • )}
; +} +``` + +### Hook Rules +- ✅ Always call hooks at top level (not in conditionals/loops) +- ✅ Only call hooks in functional components or custom hooks +- ✅ Use ESLint plugin: `eslint-plugin-react-hooks` + +## Styling + +### CSS Modules (Preferred) +```tsx +// UserCard.module.css +.card { + border: 1px solid #ccc; + border-radius: 8px; + padding: 16px; +} + +.cardTitle { + font-size: 18px; + font-weight: bold; +} + +// UserCard.tsx +import styles from './UserCard.module.css'; + +export function UserCard({ user }: { user: User }) { + return ( +
+

{user.name}

+
+ ); +} +``` + +### Tailwind CSS (Alternative) +If using Tailwind: +```tsx +export function UserCard({ user }: { user: User }) { + return ( +
+

{user.name}

+
+ ); +} +``` + +Avoid inline styles except for dynamic values: +```tsx +// OK for dynamic values +
+ +// WRONG for static styles +
+``` + +## Testing + +### React Testing Library +Focus on testing user behavior, not implementation: +```tsx +// UserProfile.test.tsx +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { UserProfile } from './UserProfile'; + +describe('UserProfile', () => { + it('displays user name and email', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.getByText('john@example.com')).toBeInTheDocument(); + }); + }); + + it('allows editing user info when Edit clicked', async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole('button', { name: /edit/i })); + + expect(screen.getByLabelText(/name/i)).toBeInTheDocument(); + }); +}); +``` + +### What to Test +- ✅ User interactions (clicks, form submissions) +- ✅ Conditional rendering based on props/state +- ✅ API integration (use MSW for mocking) +- ❌ Implementation details (state variable names, function calls) + +### Snapshot Tests +Use sparingly for stable UI only: +```tsx +it('matches snapshot for user card', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); +``` + +## Performance + +### Memoization +Use `useMemo` and `useCallback` only when needed: +```tsx +function ExpensiveComponent({ items, onSelect }: Props) { + // Memoize expensive calculation + const sortedItems = useMemo(() => { + return items.sort((a, b) => a.score - b.score); + }, [items]); + + // Memoize callback to prevent child re-renders + const handleSelect = useCallback((id: number) => { + onSelect(id); + }, [onSelect]); + + return ; +} +``` + +### React.memo +Prevent unnecessary re-renders: +```tsx +export const UserCard = React.memo(function UserCard({ user }: Props) { + return
...
; +}); +``` + +### Code Splitting +```tsx +import { lazy, Suspense } from 'react'; + +const Dashboard = lazy(() => import('./pages/Dashboard')); + +function App() { + return ( + }> + + + ); +} +``` + +## API Calls + +### Use SWR or React Query +```tsx +import useSWR from 'swr'; + +function UserProfile({ userId }: { userId: number }) { + const { data, error, isLoading } = useSWR( + `/api/users/${userId}`, + fetcher + ); + + if (isLoading) return ; + if (error) return ; + + return
{data.name}
; +} +``` + +### Benefits +- Automatic caching +- Revalidation on focus +- Request deduplication +- Error retry logic + +## Accessibility + +### Semantic HTML +```tsx +// CORRECT + + + +// WRONG - div soup +
Submit
+
+
+
navigate('/home')}>Home
+
+
+``` + +### ARIA Labels +```tsx + + + +``` + +### Keyboard Navigation +Ensure all interactive elements are keyboard accessible: +```tsx +function Modal({ onClose }: Props) { + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + + window.addEventListener('keydown', handleEscape); + return () => window.removeEventListener('keydown', handleEscape); + }, [onClose]); + + return
...
; +} +``` + +## Common Anti-Patterns + +### ❌ Prop Drilling +```tsx +// WRONG + +
+
+ + + + +// CORRECT - Use Context + + +
+
+ + +``` + +### ❌ Mutating State Directly +```tsx +// WRONG +const [items, setItems] = useState([1, 2, 3]); +items.push(4); // Direct mutation! +setItems(items); + +// CORRECT +setItems([...items, 4]); +``` + +### ❌ Missing Key Props +```tsx +// WRONG +{items.map(item =>
{item.name}
)} + +// CORRECT +{items.map(item =>
{item.name}
)} +``` + +## Tools & Configuration + +### ESLint +```json +{ + "extends": [ + "react-app", + "react-app/jest", + "plugin:react-hooks/recommended" + ] +} +``` + +### TypeScript Config +```json +{ + "compilerOptions": { + "strict": true, + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "node" + } +} +``` + +### Required Dependencies +```json +{ + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.5.0", + "eslint-plugin-react-hooks": "^4.6.0", + "typescript": "^5.0.0" + } +} +``` diff --git a/experiments/examples/centralized-rules/sync-strategy/example-central-repo/languages/python.md b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/languages/python.md new file mode 100644 index 0000000..5bf898d --- /dev/null +++ b/experiments/examples/centralized-rules/sync-strategy/example-central-repo/languages/python.md @@ -0,0 +1,283 @@ +# Python Development Rules + +Language-specific rules for Python projects. + +## Code Style + +### Formatting +- **Tool**: Black (line length: 88 characters) +- **Import sorting**: isort with Black-compatible settings +- **Linting**: Ruff (replaces flake8, pylint, pyupgrade) + +```bash +# Auto-format before commit +black . +isort . +ruff check --fix . +``` + +### Type Hints +- ✅ Required for all functions (parameters and return types) +- ✅ Use `typing` module for complex types +- ✅ Type check with mypy before committing + +```python +# CORRECT +def process_payment(user_id: int, amount: Decimal) -> PaymentResult: + ... + +# WRONG - no type hints +def process_payment(user_id, amount): + ... +``` + +### Docstrings +- Use Google-style docstrings +- Required for: all public functions, classes, modules +- Optional for: private functions, simple getters/setters + +```python +def calculate_tax(amount: Decimal, rate: Decimal) -> Decimal: + """Calculate tax amount for a transaction. + + Args: + amount: Pre-tax transaction amount in USD + rate: Tax rate as decimal (0.07 = 7%) + + Returns: + Tax amount rounded to 2 decimal places + + Raises: + ValueError: If amount or rate is negative + """ + if amount < 0 or rate < 0: + raise ValueError("Amount and rate must be non-negative") + return round(amount * rate, 2) +``` + +## Project Structure + +### Standard Layout +``` +project/ +├── src/ +│ └── myapp/ +│ ├── __init__.py +│ ├── api/ # API routes +│ ├── models/ # Data models +│ ├── services/ # Business logic +│ └── utils/ # Utilities +├── tests/ +│ ├── unit/ +│ ├── integration/ +│ └── conftest.py # Pytest fixtures +├── pyproject.toml # Dependencies & config +└── README.md +``` + +### Dependencies +- **File**: `pyproject.toml` (preferred) or `requirements.txt` +- **Pinning**: Pin all versions in production +- **Dev deps**: Separate from production (`[tool.poetry.dev-dependencies]`) + +```toml +[tool.poetry.dependencies] +python = "^3.11" +fastapi = "0.104.1" # Pin exact version +pydantic = "^2.5" # Allow minor updates + +[tool.poetry.dev-dependencies] +pytest = "^7.4" +black = "^23.11" +mypy = "^1.7" +``` + +## Testing + +### Framework +- **Primary**: pytest +- **Coverage**: pytest-cov (minimum 80%) +- **Fixtures**: Define in `tests/conftest.py` + +### Test Structure +```python +# tests/test_payments.py +import pytest +from myapp.services.payment import PaymentService + +class TestPaymentService: + """Test suite for payment processing.""" + + @pytest.fixture + def payment_service(self, db_session): + """Create payment service with test database.""" + return PaymentService(db_session) + + def test_successful_payment(self, payment_service): + """Process payment successfully with valid card.""" + # Arrange + payment_data = {"amount": 100, "card": "4242424242424242"} + + # Act + result = payment_service.process(payment_data) + + # Assert + assert result.success is True + assert result.transaction_id is not None + + def test_payment_idempotency(self, payment_service): + """Prevent duplicate charges for same transaction ID.""" + payment_id = "txn_123" + payment_service.process({"id": payment_id, "amount": 100}) + + with pytest.raises(DuplicateTransactionError): + payment_service.process({"id": payment_id, "amount": 100}) +``` + +### Coverage Requirements +```bash +# Run tests with coverage +pytest --cov=src --cov-report=term-missing --cov-fail-under=80 + +# Generate HTML report +pytest --cov=src --cov-report=html +``` + +## Common Patterns + +### Error Handling +```python +# CORRECT - Specific exceptions +try: + user = get_user_by_id(user_id) +except UserNotFoundError as e: + logger.error(f"User {user_id} not found: {e}") + raise HTTPException(status_code=404, detail="User not found") +except DatabaseError as e: + logger.error(f"Database error fetching user {user_id}: {e}") + raise HTTPException(status_code=500, detail="Database error") + +# WRONG - Bare except +try: + user = get_user_by_id(user_id) +except: # Too broad! + pass +``` + +### Logging +```python +import logging + +logger = logging.getLogger(__name__) + +# CORRECT - Structured logging with context +logger.info( + "Payment processed", + extra={ + "user_id": user_id, + "amount": amount, + "transaction_id": txn_id, + "duration_ms": duration + } +) + +# WRONG - Unstructured strings +logger.info(f"Payment of {amount} processed for user {user_id}") +``` + +### Configuration +```python +from pydantic_settings import BaseSettings + +class Settings(BaseSettings): + """Application configuration from environment.""" + + database_url: str + secret_key: str + api_timeout: int = 30 + + class Config: + env_file = ".env" + +# Load config once at startup +settings = Settings() +``` + +## Security + +### Input Validation +- Use Pydantic models for all API inputs +- Validate ranges, formats, and business rules +- Never trust user input + +```python +from pydantic import BaseModel, Field, validator + +class CreateUserRequest(BaseModel): + email: str = Field(..., regex=r"^[\w\.-]+@[\w\.-]+\.\w+$") + age: int = Field(..., ge=18, le=120) + + @validator('email') + def email_must_be_lowercase(cls, v): + return v.lower() +``` + +### SQL Injection Prevention +```python +# CORRECT - Parameterized query +cursor.execute( + "SELECT * FROM users WHERE email = %s", + (email,) # Tuple of parameters +) + +# WRONG - String interpolation +cursor.execute(f"SELECT * FROM users WHERE email = '{email}'") # SQL INJECTION! +``` + +## Performance + +### Async/Await +- Use async for I/O-bound operations (database, HTTP calls) +- Don't use async for CPU-bound tasks + +```python +async def fetch_user_data(user_id: int) -> UserData: + """Fetch user from database asynchronously.""" + async with db.acquire() as conn: + return await conn.fetchrow( + "SELECT * FROM users WHERE id = $1", + user_id + ) +``` + +### Database Queries +- Use connection pooling +- Eager load relationships (avoid N+1 queries) +- Index frequently queried columns + +## Tools & Commands + +```bash +# Format code +black src/ tests/ +isort src/ tests/ + +# Type check +mypy src/ + +# Lint +ruff check src/ tests/ + +# Test +pytest -v --cov=src + +# Security scan +bandit -r src/ +safety check +``` + +## Version Requirements + +- **Minimum Python version**: 3.11 +- **Package manager**: Poetry (preferred) or pip +- **Virtual environments**: Required for all projects diff --git a/experiments/examples/centralized-rules/sync-strategy/sync-ai-rules.sh b/experiments/examples/centralized-rules/sync-strategy/sync-ai-rules.sh new file mode 100755 index 0000000..4a80b1d --- /dev/null +++ b/experiments/examples/centralized-rules/sync-strategy/sync-ai-rules.sh @@ -0,0 +1,294 @@ +#!/bin/bash +# sync-ai-rules.sh +# Synchronize centralized AI rules to local configuration files +# Supports: CLAUDE.md, AGENTS.md, .cursorrules + +set -e + +# Configuration +CONFIG_FILE=".ai/sync-config.json" +VERSION_FILE=".ai/.rules-version" +CACHE_DIR=".ai/.rules-cache" +DEFAULT_CENTRAL_REPO="git@github.com:yourorg/ai-rules-central.git" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Flags +FORCE=false +CHECK_ONLY=false +QUIET=false + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --force) FORCE=true; shift ;; + --check-only) CHECK_ONLY=true; shift ;; + --quiet) QUIET=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +log() { + if [ "$QUIET" != "true" ]; then + echo -e "${GREEN}[sync-ai-rules]${NC} $1" + fi +} + +warn() { + echo -e "${YELLOW}[sync-ai-rules]${NC} $1" >&2 +} + +error() { + echo -e "${RED}[sync-ai-rules]${NC} $1" >&2 + exit 1 +} + +# Load configuration +load_config() { + if [ ! -f "$CONFIG_FILE" ]; then + warn "No $CONFIG_FILE found, creating default..." + mkdir -p .ai + cat > "$CONFIG_FILE" << 'EOF' +{ + "centralRepo": "git@github.com:yourorg/ai-rules-central.git", + "rules": { + "base": ["universal-rules"], + "languages": "auto-detect", + "frameworks": "auto-detect" + }, + "outputFormats": ["CLAUDE.md", "AGENTS.md", ".cursorrules"] +} +EOF + log "Created default config at $CONFIG_FILE - please customize!" + fi + + # Parse JSON config (requires jq for production, simple parsing for demo) + if command -v jq >/dev/null 2>&1; then + CENTRAL_REPO=$(jq -r '.centralRepo // empty' "$CONFIG_FILE") + OUTPUT_FORMATS=$(jq -r '.outputFormats[]' "$CONFIG_FILE" 2>/dev/null || echo "CLAUDE.md") + else + # Fallback: simple grep-based parsing + CENTRAL_REPO=$(grep -o '"centralRepo"[[:space:]]*:[[:space:]]*"[^"]*"' "$CONFIG_FILE" | cut -d'"' -f4) + OUTPUT_FORMATS="CLAUDE.md AGENTS.md .cursorrules" + fi + + CENTRAL_REPO=${CENTRAL_REPO:-$DEFAULT_CENTRAL_REPO} +} + +# Clone or update central rules repository +sync_central_repo() { + log "Syncing central rules repository..." + + if [ -d "$CACHE_DIR" ]; then + if [ "$FORCE" = "true" ]; then + log "Force mode: removing existing cache..." + rm -rf "$CACHE_DIR" + else + log "Updating existing cache..." + (cd "$CACHE_DIR" && git pull --quiet) || error "Failed to update cache" + return 0 + fi + fi + + log "Cloning $CENTRAL_REPO..." + mkdir -p .ai + git clone --depth 1 --quiet "$CENTRAL_REPO" "$CACHE_DIR" || error "Failed to clone central repo" +} + +# Detect programming languages in the repository +detect_languages() { + local languages=() + + # Python + if ls *.py pyproject.toml requirements.txt setup.py 2>/dev/null | grep -q .; then + languages+=("python") + fi + + # TypeScript + if ls tsconfig.json 2>/dev/null | grep -q . || \ + grep -q '"@types/' package.json 2>/dev/null; then + languages+=("typescript") + fi + + # JavaScript (without TypeScript) + if [ ! -f tsconfig.json ] && ls package.json 2>/dev/null | grep -q .; then + languages+=("javascript") + fi + + # Go + if ls go.mod go.sum 2>/dev/null | grep -q .; then + languages+=("go") + fi + + # Rust + if ls Cargo.toml 2>/dev/null | grep -q .; then + languages+=("rust") + fi + + # Java + if ls pom.xml build.gradle 2>/dev/null | grep -q .; then + languages+=("java") + fi + + # Ruby + if ls Gemfile *.gemspec 2>/dev/null | grep -q .; then + languages+=("ruby") + fi + + echo "${languages[@]}" +} + +# Detect frameworks +detect_frameworks() { + local frameworks=() + + # React + if grep -q '"react"' package.json 2>/dev/null; then + frameworks+=("react") + fi + + # Vue + if grep -q '"vue"' package.json 2>/dev/null; then + frameworks+=("vue") + fi + + # Next.js + if grep -q '"next"' package.json 2>/dev/null; then + frameworks+=("nextjs") + fi + + # Django + if ls manage.py 2>/dev/null | grep -q . || \ + grep -q '"django"' requirements.txt pyproject.toml 2>/dev/null; then + frameworks+=("django") + fi + + # FastAPI + if grep -q '"fastapi"' requirements.txt pyproject.toml 2>/dev/null; then + frameworks+=("fastapi") + fi + + # Express + if grep -q '"express"' package.json 2>/dev/null; then + frameworks+=("express") + fi + + # Flask + if grep -q '"flask"' requirements.txt pyproject.toml 2>/dev/null; then + frameworks+=("flask") + fi + + echo "${frameworks[@]}" +} + +# Build combined rules document +build_rules() { + local output_file=$1 + local languages=($(detect_languages)) + local frameworks=($(detect_frameworks)) + + log "Detected languages: ${languages[*]:-none}" + log "Detected frameworks: ${frameworks[*]:-none}" + + # Start document + { + cat << 'HEADER' +# AI Development Rules +# Auto-generated from central rules - DO NOT EDIT MANUALLY +HEADER + echo "# Last synced: $(date -u '+%Y-%m-%d %H:%M UTC')" + echo "# Rules version: $(cd "$CACHE_DIR" && git rev-parse --short HEAD)" + echo "# Source: $CENTRAL_REPO" + echo "" + } > "$output_file" + + # Add base rules + log "Adding base rules..." + if [ -f "$CACHE_DIR/base/universal-rules.md" ]; then + cat "$CACHE_DIR/base/universal-rules.md" >> "$output_file" + echo -e "\n---\n" >> "$output_file" + fi + + # Add language-specific rules + for lang in "${languages[@]}"; do + if [ -f "$CACHE_DIR/languages/$lang.md" ]; then + log "Adding $lang rules..." + cat "$CACHE_DIR/languages/$lang.md" >> "$output_file" + echo -e "\n---\n" >> "$output_file" + fi + done + + # Add framework-specific rules + for framework in "${frameworks[@]}"; do + if [ -f "$CACHE_DIR/frameworks/$framework.md" ]; then + log "Adding $framework rules..." + cat "$CACHE_DIR/frameworks/$framework.md" >> "$output_file" + echo -e "\n---\n" >> "$output_file" + fi + done + + # Add footer + { + echo "" + echo "---" + echo "To update these rules, run: ./sync-ai-rules.sh" + } >> "$output_file" +} + +# Check if rules are current +check_rules_current() { + if [ ! -f "$VERSION_FILE" ]; then + return 1 # No version file = outdated + fi + + local current_version=$(cat "$VERSION_FILE") + local latest_version=$(cd "$CACHE_DIR" && git rev-parse HEAD) + + if [ "$current_version" != "$latest_version" ]; then + return 1 # Version mismatch = outdated + fi + + return 0 # Current +} + +# Main sync process +main() { + log "Starting AI rules synchronization..." + + # Load configuration + load_config + + # Sync central repository + sync_central_repo + + # Check-only mode + if [ "$CHECK_ONLY" = "true" ]; then + if check_rules_current; then + log "✅ Rules are current" + exit 0 + else + error "❌ Rules are outdated. Run: ./sync-ai-rules.sh" + fi + fi + + # Generate output files + for format in $OUTPUT_FORMATS; do + log "Generating $format..." + build_rules "$format" + done + + # Update version file + (cd "$CACHE_DIR" && git rev-parse HEAD) > "$VERSION_FILE" + + log "✅ Sync complete! Generated: $OUTPUT_FORMATS" + log "Rules are now ready for:" + echo " - Claude Code (reads CLAUDE.md)" + echo " - Cursor IDE (reads .cursorrules)" + echo " - Gemini/other tools (read AGENTS.md)" +} + +main