diff --git a/.claude/agents/codebase-agent.md b/.claude/agents/codebase-agent.md index 71a8232..5dd34c2 100644 --- a/.claude/agents/codebase-agent.md +++ b/.claude/agents/codebase-agent.md @@ -21,6 +21,29 @@ color: blue You are the Codebase Agent for the Ambient Code Reference Repository. You assist with autonomous codebase operations while maintaining safety, quality, and adherence to project standards. +## CBA Workflow + +```mermaid +flowchart TB + A[Issue Received] --> B{Clear Requirements?} + B -->|No| C[Ask for Clarification] + C --> A + B -->|Yes| D[Analyze Codebase] + D --> E[Create Implementation Plan] + E --> F[Show Plan to User] + F --> G{Approved?} + G -->|No| E + G -->|Yes| H[Implement Changes] + H --> I[Run Linters & Tests] + I --> J{All Pass?} + J -->|No| H + J -->|Yes| K[Create PR] + K --> L[Wait for Human Review] + L --> M{Approved?} + M -->|Changes Requested| H + M -->|Yes| N[Merge] +``` + ## Core Capabilities ### 1. Issue-to-PR Automation diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml index 6919489..74d75af 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/docs-validation.yml @@ -30,6 +30,8 @@ jobs: else echo "No Mermaid diagrams to validate" fi + env: + PUPPETEER_CONFIG: ${{ github.workspace }}/scripts/puppeteer-config.json lint-markdown: runs-on: ubuntu-latest diff --git a/docs/architecture.md b/docs/architecture.md index 67a1929..cb5d1c2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,17 +6,37 @@ The Ambient Code Reference Repository demonstrates a **layered architecture** wi ## Layered Architecture -```text -┌─────────────────────────────────────┐ -│ API Layer (FastAPI) │ HTTP, routes, serialization -├─────────────────────────────────────┤ -│ Service Layer (Logic) │ Business rules, orchestration -├─────────────────────────────────────┤ -│ Model Layer (Pydantic) │ Validation, types -├─────────────────────────────────────┤ -│ Core Layer (Utilities) │ Config, security, logging -└─────────────────────────────────────┘ -```text +```mermaid +flowchart TB + subgraph API["API Layer (FastAPI)"] + direction LR + Routes[Routes] + Handlers[Handlers] + Serialization[Serialization] + end + subgraph Service["Service Layer (Logic)"] + direction LR + Business[Business Rules] + Orchestration[Orchestration] + end + subgraph Model["Model Layer (Pydantic)"] + direction LR + Validation[Validation] + Types[Types] + end + subgraph Core["Core Layer (Utilities)"] + direction LR + Config[Config] + Security[Security] + Logging[Logging] + end + + API --> Service + Service --> Model + Model --> Core + API --> Core + Service --> Core +``` ### API Layer @@ -39,7 +59,7 @@ def create_item(data: ItemCreate) -> Item: return item_service.create_item(data) except ValueError as e: raise HTTPException(status_code=409, detail=str(e)) -```text +``` ### Service Layer @@ -73,7 +93,7 @@ def create_item(self, data: ItemCreate) -> Item: self._next_id += 1 return item -```text +``` ### Model Layer @@ -97,7 +117,7 @@ class ItemCreate(ItemBase): @classmethod def sanitize_name(cls, v: str) -> str: return sanitize_string(v, max_length=200) -```text +``` ### Core Layer @@ -121,32 +141,32 @@ class Settings(BaseSettings): env_file = ".env" settings = Settings() -```text +``` ## Data Flow ### Creating an Item -```text -1. Client sends POST /api/v1/items - ↓ -2. API Layer (items.py) - - Pydantic validates request body - - create_item() called - ↓ -3. Service Layer (item_service.py) - - Check business rules (duplicate slug) - - Create Item model - - Store in memory - ↓ -4. Model Layer (item.py) - - Sanitize fields - - Validate types - ↓ -5. API Layer returns 201 Created - - Serialize Item to JSON - - Return to client -```text +```mermaid +sequenceDiagram + participant Client + participant API as API Layer + participant Svc as Service Layer + participant Model as Model Layer + + Client->>+API: POST /api/v1/items + API->>API: Pydantic validates request + API->>+Svc: create_item(data) + Svc->>Svc: Check business rules + Svc->>+Model: Create Item model + Model->>Model: Sanitize fields + Model->>Model: Validate types + Model-->>-Svc: Item instance + Svc->>Svc: Store in memory + Svc-->>-API: Item object + API->>API: Serialize to JSON + API-->>-Client: 201 Created + Item +``` ## Design Patterns @@ -164,7 +184,7 @@ item_service = ItemService() # app/api/v1/items.py from app.services.item_service import item_service -```text +``` ### Dependency Injection (Implicit) @@ -180,7 +200,7 @@ def create_item( service: ItemService = Depends(get_item_service) ): return service.create_item(data) -```text +``` ### Validation Pipeline @@ -197,7 +217,7 @@ class ItemCreate(ItemBase): @classmethod def validate_slug_field(cls, v: str) -> str: return validate_slug(v) -```text +``` ## Security Architecture @@ -232,21 +252,27 @@ class Settings(BaseSettings): class Config: env_file = ".env" -```text +``` ## Testing Architecture ### Test Pyramid -```text - ┌──────────┐ - │ E2E │ Few, slow (workflow tests) - ├──────────┤ - │Integration│ Some, medium (API tests) - ├──────────┤ - │ Unit │ Many, fast (service tests) - └──────────┘ -```text +```mermaid +flowchart TB + subgraph E2E["E2E Tests"] + E[Few, Slow - Workflow Tests] + end + subgraph Integration["Integration Tests"] + I[Some, Medium - API Tests] + end + subgraph Unit["Unit Tests"] + U[Many, Fast - Service Tests] + end + + E2E --> Integration + Integration --> Unit +``` **Unit**: Test service layer in isolation **Integration**: Test API with TestClient @@ -266,7 +292,7 @@ JSON format for log aggregation: "message": "Item created", "request_id": "abc123" } -```text +``` ### Health Endpoints @@ -321,7 +347,7 @@ async def add_request_id(request: Request, call_next): request.state.request_id = generate_id() response = await call_next(request) return response -```text +``` ### Adding Database @@ -330,7 +356,7 @@ async def add_request_id(request: Request, call_next): from sqlalchemy.ext.asyncio import create_async_engine engine = create_async_engine(settings.database_url) -```text +``` ## Best Practices diff --git a/docs/quickstart.md b/docs/quickstart.md index c004731..b2dc5db 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -12,6 +12,23 @@ This is a **documentation-only reference** for AI-assisted development patterns. **Looking for a working application?** See [demo-fastapi](https://github.com/jeremyeder/demo-fastapi) +### Getting Started Flow + +```mermaid +flowchart LR + A[Clone Repo] --> B[Explore Docs] + B --> C{What do you need?} + C -->|CBA Setup| D[Copy .claude/] + C -->|Architecture| E[Read docs/architecture.md] + C -->|Testing| F[Read testing-patterns.md] + C -->|CI/CD| G[Copy .github/workflows/] + D --> H[Customize for your project] + E --> H + F --> H + G --> H + H --> I[Ship It] +``` + ## Prerequisites - Python 3.11 or 3.12 (for documentation tooling) diff --git a/scripts/puppeteer-config.json b/scripts/puppeteer-config.json new file mode 100644 index 0000000..2274c80 --- /dev/null +++ b/scripts/puppeteer-config.json @@ -0,0 +1,3 @@ +{ + "args": ["--no-sandbox", "--disable-setuid-sandbox"] +} diff --git a/scripts/validate-mermaid.sh b/scripts/validate-mermaid.sh index fd45c6a..83958b1 100755 --- a/scripts/validate-mermaid.sh +++ b/scripts/validate-mermaid.sh @@ -1,40 +1,144 @@ #!/bin/bash set -e -echo "Validating Mermaid diagrams..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT -# Find all .mmd files -MMD_FILES=$(find docs -name "*.mmd" 2>/dev/null || true) +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' -if [ -z "$MMD_FILES" ]; then - echo "No .mmd files found. Skipping Mermaid validation." - exit 0 -fi +echo "Validating Mermaid diagrams..." +echo "" -# Check if mmdc is installed if ! command -v mmdc &> /dev/null; then - echo "Error: mermaid-cli (mmdc) is not installed" + echo -e "${YELLOW}Warning: mermaid-cli (mmdc) is not installed${NC}" echo "Install with: npm install -g @mermaid-js/mermaid-cli" - exit 1 + echo "" + echo "Falling back to syntax-only validation..." + MMDC_AVAILABLE=false +else + MMDC_AVAILABLE=true +fi + +MMDC_ARGS="" +if [[ -n "$PUPPETEER_CONFIG" ]] && [[ -f "$PUPPETEER_CONFIG" ]]; then + MMDC_ARGS="-p $PUPPETEER_CONFIG" + echo "Using Puppeteer config: $PUPPETEER_CONFIG" +elif [[ -f "$SCRIPT_DIR/puppeteer-config.json" ]]; then + MMDC_ARGS="-p $SCRIPT_DIR/puppeteer-config.json" + echo "Using Puppeteer config: $SCRIPT_DIR/puppeteer-config.json" fi -# Validate each diagram ERRORS=0 -for file in $MMD_FILES; do - echo "Validating $file..." - if ! mmdc -i "$file" -o /dev/null 2>&1; then - echo "✗ Error in $file" - ERRORS=$((ERRORS + 1)) - else - echo "✓ $file is valid" - fi -done +TOTAL=0 -if [ $ERRORS -gt 0 ]; then - echo "" - echo "Found $ERRORS invalid Mermaid diagram(s)" - exit 1 +validate_markdown_file() { + local file="$1" + local block_num=0 + local in_mermaid=false + local mermaid_content="" + local line_num=0 + local block_start_line=0 + + while IFS= read -r line || [[ -n "$line" ]]; do + line_num=$((line_num + 1)) + + if [[ "$line" =~ ^\`\`\`mermaid ]]; then + in_mermaid=true + mermaid_content="" + block_start_line=$line_num + continue + fi + + if [[ "$in_mermaid" == true ]]; then + if [[ "$line" =~ ^\`\`\` ]]; then + in_mermaid=false + block_num=$((block_num + 1)) + TOTAL=$((TOTAL + 1)) + + local temp_file="$TEMP_DIR/diagram_${block_num}.mmd" + echo "$mermaid_content" > "$temp_file" + + if [[ "$MMDC_AVAILABLE" == true ]]; then + if mmdc $MMDC_ARGS -i "$temp_file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + echo -e "${GREEN}✓${NC} $file:$block_start_line (block $block_num)" + else + echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" + echo " Error: $(head -3 "$TEMP_DIR/error.log")" + ERRORS=$((ERRORS + 1)) + fi + else + local has_error=false + if ! echo "$mermaid_content" | head -1 | grep -qE '^[[:space:]]*(flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|gitGraph|journey|mindmap|timeline|quadrantChart|sankey|xychart|block)'; then + echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" + echo " Error: No valid diagram type found on first line" + has_error=true + ERRORS=$((ERRORS + 1)) + fi + + if [[ "$has_error" == false ]]; then + echo -e "${YELLOW}?${NC} $file:$block_start_line (block $block_num) - syntax check only" + fi + fi + else + mermaid_content+="$line"$'\n' + fi + fi + done < "$file" +} + +echo "=== Standalone .mmd files ===" +MMD_FILES=$(find "$REPO_ROOT" -name "*.mmd" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) + +if [ -z "$MMD_FILES" ]; then + echo "No standalone .mmd files found" +else + for file in $MMD_FILES; do + TOTAL=$((TOTAL + 1)) + if [[ "$MMDC_AVAILABLE" == true ]]; then + if mmdc $MMDC_ARGS -i "$file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + echo -e "${GREEN}✓${NC} $file" + else + echo -e "${RED}✗${NC} $file" + echo " Error: $(head -3 "$TEMP_DIR/error.log")" + ERRORS=$((ERRORS + 1)) + fi + else + echo -e "${YELLOW}?${NC} $file - skipped (mmdc not available)" + fi + done +fi + +echo "" +echo "=== Inline mermaid blocks in markdown ===" + +MD_FILES=$(find "$REPO_ROOT" -name "*.md" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) + +if [ -z "$MD_FILES" ]; then + echo "No markdown files found" +else + for file in $MD_FILES; do + if grep -q '```mermaid' "$file" 2>/dev/null; then + validate_markdown_file "$file" + fi + done fi echo "" -echo "All Mermaid diagrams are valid! ✓" +echo "=== Summary ===" +echo "Total diagrams checked: $TOTAL" + +if [ $ERRORS -gt 0 ]; then + echo -e "${RED}Errors found: $ERRORS${NC}" + exit 1 +elif [ $TOTAL -eq 0 ]; then + echo "No Mermaid diagrams found to validate" + exit 0 +else + echo -e "${GREEN}All $TOTAL diagram(s) valid!${NC}" + exit 0 +fi