gantt-board/CLI_STANDARD.md

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

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

  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