# 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 <id>           # Get single item
./scripts/crud.sh create <json>      # Create item
./scripts/crud.sh update <id> <field> <value>  # Update field
./scripts/crud.sh delete <id>        # Delete item
./scripts/attach-file.sh <id> <path> # 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 <id>|create <json>|update <id> <field> <value>|delete <id>}"
    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 <item-id> <file-path>"
  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
