6.1 KiB
6.1 KiB
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:
# 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):
# 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
#!/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
#!/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
- Create
scripts/directory in project root - Copy template scripts from gantt-board
- Update table names and fields for your project
- Create
scripts/.envwith Supabase credentials - Test:
./scripts/crud.sh list - Commit to git:
git add scripts/ && git commit -m "Add CLI tools for AI access" - Update project's README.md with CLI usage section
Agent Instructions
When working on a project:
- Check for
scripts/directory in project root - If CLI exists, use it:
./scripts/crud.sh list - If CLI doesn't exist, create it using patterns above
- 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) .envfile exists with credentials (gitignored)- Scripts work:
./scripts/crud.sh listreturns data - Changes are committed to git
- Project README documents CLI usage