# CLI Integration Standard Standard for adding programmatic CLI access to all projects so AI agents can work without browser automation. ## Rule: CLI Tools Live INSIDE the Project **Location:** `{project-root}/scripts/` **Why:** - Version controlled with the project - Self-documenting (API + CLI in same repo) - Portable (clone repo, CLI works immediately) - Project-specific logic stays with project - No hunting around workspace folders ## Required File Structure ``` project-root/ ├── scripts/ │ ├── crud.sh # Main CRUD operations │ ├── attach-file.sh # File attachments │ ├── README.md # Usage documentation │ └── .env.example # Required environment variables ├── api/ # REST API routes (used by CLI) │ └── items/ │ ├── route.ts │ └── [id]/ │ ├── route.ts │ └── attachments/ │ └── route.ts └── README.md # Project README with CLI section ``` ## Required CLI Commands Every project MUST implement these: ```bash # From project root: ./scripts/crud.sh list [filters] # List all items ./scripts/crud.sh get # Get single item ./scripts/crud.sh create # Create item ./scripts/crud.sh update # Update field ./scripts/crud.sh delete # Delete item ./scripts/attach-file.sh # Attach file to item ``` ## Environment Variables Each project's CLI needs these in `scripts/.env` (gitignored): ```bash # Supabase direct access (for bypassing auth) SUPABASE_URL=https://xxxx.supabase.co SUPABASE_SERVICE_ROLE_KEY=eyJ... # Or API-based access API_URL=http://localhost:3000/api API_KEY=secret_key_for_cli_access ``` ## Supabase Direct Pattern (Recommended) For Supabase projects, use direct DB access with service role key: **File:** `scripts/crud.sh` ```bash #!/bin/bash set -e # Load env from scripts/.env source "$(dirname "$0")/.env" action=$1 shift case $action in list) curl -s "${SUPABASE_URL}/rest/v1/items?select=*" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" | jq '.' ;; get) id=$1 curl -s "${SUPABASE_URL}/rest/v1/items?id=eq.${id}" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" | jq '.[0]' ;; create) data=$1 curl -s -X POST "${SUPABASE_URL}/rest/v1/items" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Content-Type: application/json" \ -d "$data" | jq '.' ;; update) id=$1 field=$2 value=$3 curl -s -X PATCH "${SUPABASE_URL}/rest/v1/items?id=eq.${id}" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Content-Type: application/json" \ -d "{\"${field}\": \"${value}\"}" | jq '.' ;; delete) id=$1 curl -s -X DELETE "${SUPABASE_URL}/rest/v1/items?id=eq.${id}" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" echo "Deleted ${id}" ;; *) echo "Usage: $0 {list|get |create |update |delete }" exit 1 ;; esac ``` ## File Attachment Pattern **File:** `scripts/attach-file.sh` ```bash #!/bin/bash set -e source "$(dirname "$0")/.env" ITEM_ID=$1 FILE_PATH=$2 if [ -z "$ITEM_ID" ] || [ -z "$FILE_PATH" ]; then echo "Usage: $0 " exit 1 fi if [ ! -f "$FILE_PATH" ]; then echo "Error: File not found: $FILE_PATH" exit 1 fi # Read and encode file mime=$(file -b --mime-type "$FILE_PATH") base64=$(base64 -i "$FILE_PATH") filename=$(basename "$FILE_PATH") size=$(stat -f%z "$FILE_PATH" 2>/dev/null || stat -c%s "$FILE_PATH") # Build attachment JSON attachment=$(jq -n \ --arg id "$(uuidgen | tr '[:upper:]' '[:lower:]')" \ --arg name "$filename" \ --arg type "$mime" \ --arg data "data:${mime};base64,${base64}" \ --argjson size "$size" \ '{id: $id, name: $name, type: $type, dataUrl: $data, size: $size}') # Get current attachments, append new one current=$(curl -s "${SUPABASE_URL}/rest/v1/items?id=eq.${ITEM_ID}&select=attachments" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" | jq '.[0].attachments // []') updated=$(echo "$current" | jq ". + [$attachment]") # Update item curl -s -X PATCH "${SUPABASE_URL}/rest/v1/items?id=eq.${ITEM_ID}" \ -H "apikey: ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Authorization: Bearer ${SUPABASE_SERVICE_ROLE_KEY}" \ -H "Content-Type: application/json" \ -d "{\"attachments\": $updated}" | jq '.' echo "✅ Attached ${filename} (${size} bytes) to item ${ITEM_ID}" ``` ## Per-Project Reference ### Gantt Board - **Location:** `/Users/mattbruce/Documents/Projects/OpenClaw/Web/gantt-board/scripts/` - **Scripts:** `gantt-task-crud.sh`, `attach-file.sh`, `view-attachment.sh` - **Auth:** Supabase service role key - **Table:** `public.tasks` ### Adding CLI to a New Project 1. Create `scripts/` directory in project root 2. Copy template scripts from gantt-board 3. Update table names and fields for your project 4. Create `scripts/.env` with Supabase credentials 5. Test: `./scripts/crud.sh list` 6. Commit to git: `git add scripts/ && git commit -m "Add CLI tools for AI access"` 7. Update project's README.md with CLI usage section ## Agent Instructions When working on a project: 1. Check for `scripts/` directory in project root 2. If CLI exists, use it: `./scripts/crud.sh list` 3. If CLI doesn't exist, create it using patterns above 4. NEVER create files in workspace scripts folder for project-specific tools ## Verification Checklist Before saying a task is done: - [ ] CLI tools are in `{project}/scripts/`, NOT workspace - [ ] Scripts are executable (`chmod +x`) - [ ] `.env` file exists with credentials (gitignored) - [ ] Scripts work: `./scripts/crud.sh list` returns data - [ ] Changes are committed to git - [ ] Project README documents CLI usage