756 lines
19 KiB
Bash
Executable File
756 lines
19 KiB
Bash
Executable File
#!/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:-https://gantt-board.twisteddevices.com/api}"
|
||
COOKIE_FILE="${GANTT_COOKIE_FILE:-$HOME/.config/gantt-board/cookies.txt}"
|
||
|
||
# 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
|
||
}
|
||
|
||
# Machine-to-machine API call (for cron/automation)
|
||
api_call_machine() {
|
||
local method="$1"
|
||
local endpoint="$2"
|
||
local data="${3:-}"
|
||
|
||
local token="${GANTT_MACHINE_TOKEN:-}"
|
||
if [ -z "$token" ]; then
|
||
log_error "GANTT_MACHINE_TOKEN not set"
|
||
return 1
|
||
fi
|
||
|
||
local url="${API_URL}${endpoint}"
|
||
local curl_opts=(-s -w "\n%{http_code}" -H "Content-Type: application/json" -H "Authorization: Bearer ${token}")
|
||
|
||
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
|
||
}
|
||
|
||
# API call helper (cookie auth for interactive, machine token for automation)
|
||
api_call() {
|
||
local method="$1"
|
||
local endpoint="$2"
|
||
local data="${3:-}"
|
||
|
||
# Machine token path for automation/cron
|
||
if [ -n "${GANTT_MACHINE_TOKEN:-}" ]; then
|
||
api_call_machine "$method" "$endpoint" "$data"
|
||
return $?
|
||
fi
|
||
|
||
# Cookie auth path for interactive use
|
||
local url="${API_URL}${endpoint}"
|
||
|
||
mkdir -p "$(dirname "$COOKIE_FILE")"
|
||
touch "$COOKIE_FILE"
|
||
|
||
local curl_opts=(-s -w "\n%{http_code}" -H "Content-Type: application/json" -b "$COOKIE_FILE" -c "$COOKIE_FILE")
|
||
|
||
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, commentAuthorId: "assistant", replies: []}')
|
||
|
||
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..."
|
||
api_call GET "/projects" | jq '.projects'
|
||
}
|
||
|
||
cmd_project_get() {
|
||
local project_id="$1"
|
||
|
||
if [ -z "$project_id" ]; then
|
||
log_error "Usage: project get <project-id>"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Fetching project $project_id..."
|
||
api_call GET "/projects/$project_id" | jq '.project'
|
||
}
|
||
|
||
cmd_project_create() {
|
||
local name="$1"
|
||
local description="${2:-}"
|
||
local color="${3:-#3b82f6}"
|
||
|
||
if [ -z "$name" ]; then
|
||
log_error "Usage: project create <name> [description] [color]"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Creating project: $name"
|
||
local data
|
||
data=$(jq -n --arg name "$name" --arg desc "$description" --arg color "$color" \
|
||
'{name: $name, description: (if $desc == "" then null else $desc end), color: $color}')
|
||
api_call POST "/projects" "$data"
|
||
}
|
||
|
||
cmd_project_update() {
|
||
local project_id="$1"
|
||
local field="$2"
|
||
local value="$3"
|
||
|
||
if [ -z "$project_id" ] || [ -z "$field" ]; then
|
||
log_error "Usage: project update <id> <field> <value>"
|
||
echo "Fields: name, description, color"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Updating project $project_id: $field = $value"
|
||
local data
|
||
data=$(jq -n --arg id "$project_id" --arg field "$field" --arg value "$value" \
|
||
'{id: $id, ($field): $value}')
|
||
api_call PATCH "/projects" "$data"
|
||
}
|
||
|
||
cmd_project_delete() {
|
||
local project_id="$1"
|
||
|
||
if [ -z "$project_id" ]; then
|
||
log_error "Usage: project delete <id>"
|
||
exit 1
|
||
fi
|
||
|
||
log_warn "Deleting project $project_id..."
|
||
api_call DELETE "/projects" "{\"id\": \"$project_id\"}"
|
||
}
|
||
|
||
#===================
|
||
# SPRINT OPERATIONS
|
||
#===================
|
||
|
||
cmd_sprint_list() {
|
||
log_info "Fetching sprints..."
|
||
api_call GET "/sprints" | jq '.sprints'
|
||
}
|
||
|
||
cmd_sprint_get() {
|
||
local sprint_id="$1"
|
||
|
||
if [ -z "$sprint_id" ]; then
|
||
log_error "Usage: sprint get <sprint-id>"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Fetching sprint $sprint_id..."
|
||
api_call GET "/sprints/$sprint_id" | jq '.sprint'
|
||
}
|
||
|
||
cmd_sprint_create() {
|
||
local name="$1"
|
||
local start_date="${2:-}"
|
||
local end_date="${3:-}"
|
||
local goal="${4:-}"
|
||
|
||
if [ -z "$name" ]; then
|
||
log_error "Usage: sprint create <name> [start-date] [end-date] [goal]"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Creating sprint: $name"
|
||
local data
|
||
data=$(jq -n \
|
||
--arg name "$name" \
|
||
--arg startDate "$start_date" \
|
||
--arg endDate "$end_date" \
|
||
--arg goal "$goal" \
|
||
'{
|
||
name: $name,
|
||
startDate: (if $startDate == "" then null else $startDate end),
|
||
endDate: (if $endDate == "" then null else $endDate end),
|
||
goal: (if $goal == "" then null else $goal end)
|
||
}')
|
||
api_call POST "/sprints" "$data"
|
||
}
|
||
|
||
cmd_sprint_update() {
|
||
local sprint_id="$1"
|
||
local field="$2"
|
||
local value="$3"
|
||
|
||
if [ -z "$sprint_id" ] || [ -z "$field" ]; then
|
||
log_error "Usage: sprint update <id> <field> <value>"
|
||
echo "Fields: name, goal, startDate, endDate"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$field" = "status" ]; then
|
||
log_error "Sprint status is date-derived. Update startDate/endDate instead."
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Updating sprint $sprint_id: $field = $value"
|
||
local data
|
||
data=$(jq -n --arg id "$sprint_id" --arg field "$field" --arg value "$value" \
|
||
'{id: $id, ($field): $value}')
|
||
api_call PATCH "/sprints" "$data"
|
||
}
|
||
|
||
cmd_sprint_delete() {
|
||
local sprint_id="$1"
|
||
|
||
if [ -z "$sprint_id" ]; then
|
||
log_error "Usage: sprint delete <id>"
|
||
exit 1
|
||
fi
|
||
|
||
log_warn "Deleting sprint $sprint_id..."
|
||
api_call DELETE "/sprints" "{\"id\": \"$sprint_id\"}"
|
||
}
|
||
|
||
|
||
cmd_sprint_close() {
|
||
local sprint_id="$1"
|
||
|
||
if [ -z "$sprint_id" ]; then
|
||
log_error "Usage: sprint close <id>"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Closing sprint $sprint_id..."
|
||
api_call POST "/sprints/close" "{\"id\": \"$sprint_id\"}"
|
||
}
|
||
|
||
#===================
|
||
# 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"
|
||
}
|
||
|
||
cmd_auth_register() {
|
||
local email="$1"
|
||
local password="$2"
|
||
local name="${3:-}"
|
||
|
||
if [ -z "$email" ] || [ -z "$password" ]; then
|
||
log_error "Usage: auth register <email> <password> [name]"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Registering user..."
|
||
local data
|
||
data=$(jq -n --arg email "$email" --arg password "$password" --arg name "$name" \
|
||
'{email: $email, password: $password, name: (if $name == "" then null else $name end)}')
|
||
api_call POST "/auth/register" "$data"
|
||
}
|
||
|
||
cmd_auth_forgot_password() {
|
||
local email="$1"
|
||
|
||
if [ -z "$email" ]; then
|
||
log_error "Usage: auth forgot-password <email>"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Requesting password reset..."
|
||
api_call POST "/auth/forgot-password" "{\"email\": \"$email\"}"
|
||
}
|
||
|
||
cmd_auth_reset_password() {
|
||
local token="$1"
|
||
local password="$2"
|
||
|
||
if [ -z "$token" ] || [ -z "$password" ]; then
|
||
log_error "Usage: auth reset-password <token> <new-password>"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Resetting password..."
|
||
api_call POST "/auth/reset-password" "{\"token\": \"$token\", \"password\": \"$password\"}"
|
||
}
|
||
|
||
cmd_auth_account() {
|
||
local field="$1"
|
||
local value="$2"
|
||
|
||
if [ -z "$field" ] || [ -z "$value" ]; then
|
||
log_error "Usage: auth account <field> <value>"
|
||
echo "Fields: name, email"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Updating account $field..."
|
||
local data
|
||
data=$(jq -n --arg field "$field" --arg value "$value" '{($field): $value}')
|
||
api_call PATCH "/auth/account" "$data"
|
||
}
|
||
|
||
cmd_auth_users() {
|
||
log_info "Fetching users..."
|
||
api_call GET "/auth/users"
|
||
}
|
||
|
||
#===================
|
||
# 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
|
||
project get <id> Get specific project
|
||
project create <name> [desc] [color]
|
||
Create new project
|
||
project update <id> <field> <val>
|
||
Update project field
|
||
Fields: name, description, color
|
||
project delete <id> Delete a project
|
||
|
||
SPRINT COMMANDS:
|
||
sprint list List all sprints
|
||
sprint get <id> Get specific sprint
|
||
sprint create <name> [start] [end] [goal]
|
||
Create new sprint
|
||
sprint update <id> <field> <val>
|
||
Update sprint field
|
||
Fields: name, goal, startDate, endDate
|
||
sprint close <id> Close a sprint
|
||
sprint delete <id> Delete a sprint
|
||
|
||
AUTH COMMANDS:
|
||
auth login <email> <pass> Log in
|
||
auth logout Log out
|
||
auth session Check current session
|
||
auth register <email> <pass> Register new account
|
||
auth forgot-password <email> Request password reset
|
||
auth reset-password <tok> <pass> Reset password with token
|
||
auth account <field> <value> Update account (name, email)
|
||
auth users List all users
|
||
|
||
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 "$@" ;;
|
||
get|show) cmd_project_get "$@" ;;
|
||
create|new|add) cmd_project_create "$@" ;;
|
||
update|set|edit) cmd_project_update "$@" ;;
|
||
delete|rm|remove) cmd_project_delete "$@" ;;
|
||
*) 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 "$@" ;;
|
||
get|show) cmd_sprint_get "$@" ;;
|
||
create|new|add) cmd_sprint_create "$@" ;;
|
||
update|set|edit) cmd_sprint_update "$@" ;;
|
||
close|complete) cmd_sprint_close "$@" ;;
|
||
delete|rm|remove) cmd_sprint_delete "$@" ;;
|
||
*) 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 "$@" ;;
|
||
register|signup) cmd_auth_register "$@" ;;
|
||
forgot-password|forgot) cmd_auth_forgot_password "$@" ;;
|
||
reset-password|reset) cmd_auth_reset_password "$@" ;;
|
||
account|profile) cmd_auth_account "$@" ;;
|
||
users|list-users) cmd_auth_users "$@" ;;
|
||
*) 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 "$@"
|