gantt-board/scripts/gantt.sh
Max faeee26222 Add unified CLI and update documentation
- New gantt.sh: Complete CLI covering all API operations
  - Task CRUD (list, get, create, update, delete)
  - Natural language task creation
  - Comments and file attachments
  - Projects, sprints, auth
- Updated README.md with comprehensive coverage matrix
- Documents when to use API vs Supabase direct scripts
2026-02-21 17:28:43 -06:00

468 lines
11 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Gantt Board Complete CLI
# All API operations available to the web UI
# Usage: ./gantt.sh <command> [args]
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
API_URL="${API_URL:-http://localhost:3000/api}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
log_info() { echo -e "${BLUE}${NC} $1"; }
log_success() { echo -e "${GREEN}${NC} $1"; }
log_error() { echo -e "${RED}${NC} $1"; }
log_warn() { echo -e "${YELLOW}${NC} $1"; }
# Check dependencies
check_deps() {
if ! command -v jq &> /dev/null; then
log_error "jq is required but not installed. Run: brew install jq"
exit 1
fi
if ! command -v curl &> /dev/null; then
log_error "curl is required but not installed"
exit 1
fi
}
# API call helper
api_call() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="${API_URL}${endpoint}"
local curl_opts=(-s -w "\n%{http_code}" -H "Content-Type: application/json")
if [ -n "$data" ]; then
curl_opts+=(-d "$data")
fi
local response
response=$(curl "${curl_opts[@]}" -X "$method" "$url")
local http_code
http_code=$(echo "$response" | tail -n1)
local body
body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
echo "$body"
return 0
else
log_error "API call failed (HTTP $http_code)"
echo "$body" | jq '.' 2>/dev/null || echo "$body"
return 1
fi
}
#===================
# TASK OPERATIONS
#===================
cmd_task_list() {
local filter="${1:-}"
log_info "Fetching tasks..."
local response
response=$(api_call GET "/tasks")
if [ -n "$filter" ]; then
echo "$response" | jq --arg status "$filter" '.tasks | map(select(.status == $status))'
else
echo "$response" | jq '.tasks'
fi
}
cmd_task_get() {
local task_id="$1"
if [ -z "$task_id" ]; then
log_error "Usage: task get <task-id>"
exit 1
fi
log_info "Fetching task $task_id..."
local response
response=$(api_call GET "/tasks")
echo "$response" | jq --arg id "$task_id" '.tasks | map(select(.id == $id)) | .[0]'
}
cmd_task_create() {
local title="$1"
local status="${2:-open}"
local priority="${3:-medium}"
local project_id="${4:-1}"
if [ -z "$title" ]; then
log_error "Usage: task create <title> [status] [priority] [project-id]"
exit 1
fi
log_info "Creating task: $title"
local task_json
task_json=$(jq -n \
--arg title "$title" \
--arg status "$status" \
--arg priority "$priority" \
--arg projectId "$project_id" \
'{
title: $title,
status: $status,
priority: $priority,
projectId: $projectId,
type: "task",
comments: [],
tags: []
}')
api_call POST "/tasks" "{\"task\": $task_json}"
}
cmd_task_natural() {
local text="$1"
if [ -z "$text" ]; then
log_error "Usage: task natural <text-description>"
echo "Example: ./gantt.sh task natural \"Fix login bug by Friday, high priority\""
exit 1
fi
log_info "Creating task from natural language..."
api_call POST "/tasks/natural" "{\"text\": \"$text\"}"
}
cmd_task_update() {
local task_id="$1"
local field="$2"
local value="$3"
if [ -z "$task_id" ] || [ -z "$field" ]; then
log_error "Usage: task update <task-id> <field> <value>"
echo "Fields: status, priority, title, description, assigneeId, sprintId, dueDate"
exit 1
fi
log_info "Updating task $task_id: $field = $value"
# First get the task
local response
response=$(api_call GET "/tasks")
local task
task=$(echo "$response" | jq --arg id "$task_id" '.tasks | map(select(.id == $id)) | .[0]')
if [ "$task" = "null" ] || [ -z "$task" ]; then
log_error "Task not found: $task_id"
exit 1
fi
# Update the field
local updated_task
updated_task=$(echo "$task" | jq --arg field "$field" --arg value "$value" '. + {($field): $value}')
api_call POST "/tasks" "{\"task\": $updated_task}"
}
cmd_task_delete() {
local task_id="$1"
if [ -z "$task_id" ]; then
log_error "Usage: task delete <task-id>"
exit 1
fi
log_warn "Deleting task $task_id..."
api_call DELETE "/tasks" "{\"id\": \"$task_id\"}"
}
cmd_task_comment() {
local task_id="$1"
local text="$2"
if [ -z "$task_id" ] || [ -z "$text" ]; then
log_error "Usage: task comment <task-id> <text>"
exit 1
fi
log_info "Adding comment to task $task_id..."
# Get current task
local response
response=$(api_call GET "/tasks")
local task
task=$(echo "$response" | jq --arg id "$task_id" '.tasks | map(select(.id == $id)) | .[0]')
if [ "$task" = "null" ]; then
log_error "Task not found: $task_id"
exit 1
fi
# Add comment
local comment_id
comment_id=$(date +%s)
local new_comment
new_comment=$(jq -n \
--arg id "$comment_id" \
--arg text "$text" \
--arg createdAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{id: $id, text: $text, createdAt: $createdAt, author: "assistant"}')
local updated_task
updated_task=$(echo "$task" | jq --argjson comment "$new_comment" '.comments += [$comment]')
api_call POST "/tasks" "{\"task\": $updated_task}"
}
cmd_task_attach() {
local task_id="$1"
local file_path="$2"
if [ -z "$task_id" ] || [ -z "$file_path" ]; then
log_error "Usage: task attach <task-id> <file-path>"
exit 1
fi
if [ ! -f "$file_path" ]; then
log_error "File not found: $file_path"
exit 1
fi
log_info "Attaching file to task $task_id..."
# Get current task
local response
response=$(api_call GET "/tasks")
local task
task=$(echo "$response" | jq --arg id "$task_id" '.tasks | map(select(.id == $id)) | .[0]')
if [ "$task" = "null" ]; then
log_error "Task not found: $task_id"
exit 1
fi
# Create attachment
local filename
filename=$(basename "$file_path")
local mime_type
case "${filename##*.}" in
md|markdown) mime_type="text/markdown" ;;
txt) mime_type="text/plain" ;;
json) mime_type="application/json" ;;
pdf) mime_type="application/pdf" ;;
png) mime_type="image/png" ;;
jpg|jpeg) mime_type="image/jpeg" ;;
gif) mime_type="image/gif" ;;
*) mime_type="application/octet-stream" ;;
esac
local base64_content
base64_content=$(base64 -i "$file_path" | tr -d '\n')
local data_url="data:$mime_type;base64,$base64_content"
local attachment
attachment=$(jq -n \
--arg id "$(date +%s)" \
--arg name "$filename" \
--arg type "$mime_type" \
--argjson size "$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path")" \
--arg dataUrl "$data_url" \
--arg uploadedAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{id: $id, name: $name, type: $type, size: $size, dataUrl: $dataUrl, uploadedAt: $uploadedAt}')
local updated_task
updated_task=$(echo "$task" | jq --argjson att "$attachment" '.attachments = (.attachments // []) + [$att]')
api_call POST "/tasks" "{\"task\": $updated_task}"
}
#===================
# PROJECT OPERATIONS
#===================
cmd_project_list() {
log_info "Fetching projects..."
local response
response=$(api_call GET "/tasks")
echo "$response" | jq '.projects'
}
#===================
# SPRINT OPERATIONS
#===================
cmd_sprint_list() {
log_info "Fetching sprints..."
local response
response=$(api_call GET "/tasks")
echo "$response" | jq '.sprints'
}
#===================
# AUTH OPERATIONS
#===================
cmd_auth_login() {
local email="$1"
local password="$2"
if [ -z "$email" ] || [ -z "$password" ]; then
log_error "Usage: auth login <email> <password>"
exit 1
fi
log_info "Logging in..."
api_call POST "/auth/login" "{\"email\": \"$email\", \"password\": \"$password\"}"
}
cmd_auth_logout() {
log_info "Logging out..."
api_call POST "/auth/logout" "{}"
}
cmd_auth_session() {
log_info "Checking session..."
api_call GET "/auth/session"
}
#===================
# DEBUG OPERATIONS
#===================
cmd_debug() {
log_info "Calling debug endpoint..."
api_call GET "/debug"
}
#===================
# HELP
#===================
show_help() {
cat << 'EOF'
Gantt Board CLI - Complete API Access
USAGE:
./gantt.sh <command> [subcommand] [args]
TASK COMMANDS:
task list [status] List all tasks (optionally filter by status)
task get <id> Get specific task details
task create <title> [status] [priority] [project-id]
Create a new task
task natural <text> Create task from natural language
Example: "Fix bug by Friday, high priority"
task update <id> <field> <val> Update task field
Fields: status, priority, title, description,
assigneeId, sprintId, dueDate
task delete <id> Delete a task
task comment <id> <text> Add a comment to a task
task attach <id> <file> Attach a file to a task
PROJECT COMMANDS:
project list List all projects
SPRINT COMMANDS:
sprint list List all sprints
AUTH COMMANDS:
auth login <email> <pass> Log in
auth logout Log out
auth session Check current session
OTHER COMMANDS:
debug Call debug endpoint
help Show this help message
EXAMPLES:
# List open tasks
./gantt.sh task list open
# Create a task naturally
./gantt.sh task natural "Research TTS options by tomorrow, medium priority"
# Update task status
./gantt.sh task update abc-123 status done
# Add comment
./gantt.sh task comment abc-123 "Working on this now"
# Attach file
./gantt.sh task attach abc-123 ./notes.md
ENVIRONMENT:
API_URL Override the API base URL (default: http://localhost:3000/api)
EOF
}
#===================
# MAIN
#===================
main() {
check_deps
local cmd="${1:-help}"
shift || true
case "$cmd" in
task)
local subcmd="${1:-list}"
shift || true
case "$subcmd" in
list|ls) cmd_task_list "$@" ;;
get|show) cmd_task_get "$@" ;;
create|new|add) cmd_task_create "$@" ;;
natural|parse) cmd_task_natural "$@" ;;
update|set|edit) cmd_task_update "$@" ;;
delete|rm|remove) cmd_task_delete "$@" ;;
comment|note) cmd_task_comment "$@" ;;
attach|file) cmd_task_attach "$@" ;;
*) log_error "Unknown task command: $subcmd"; show_help; exit 1 ;;
esac
;;
project|projects)
local subcmd="${1:-list}"
shift || true
case "$subcmd" in
list|ls) cmd_project_list "$@" ;;
*) log_error "Unknown project command: $subcmd"; show_help; exit 1 ;;
esac
;;
sprint|sprints)
local subcmd="${1:-list}"
shift || true
case "$subcmd" in
list|ls) cmd_sprint_list "$@" ;;
*) log_error "Unknown sprint command: $subcmd"; show_help; exit 1 ;;
esac
;;
auth)
local subcmd="${1:-session}"
shift || true
case "$subcmd" in
login) cmd_auth_login "$@" ;;
logout) cmd_auth_logout "$@" ;;
session|whoami) cmd_auth_session "$@" ;;
*) log_error "Unknown auth command: $subcmd"; show_help; exit 1 ;;
esac
;;
debug) cmd_debug "$@" ;;
help|--help|-h) show_help ;;
*) log_error "Unknown command: $cmd"; show_help; exit 1 ;;
esac
}
main "$@"