#!/usr/bin/env bash set -euo pipefail # Mosaic Brain CLI - Interface to Mosaic Stack Ideas/Brain API # Usage: brain.sh [options] # Load configuration CONFIG_FILE="${HOME}/.config/mosaic/brain.conf" if [[ -f "$CONFIG_FILE" ]]; then # shellcheck source=/dev/null source "$CONFIG_FILE" fi # Configuration with defaults API_URL="${MOSAIC_API_URL:-http://localhost:3001}" WORKSPACE_ID="${MOSAIC_WORKSPACE_ID:-}" API_TOKEN="${MOSAIC_API_TOKEN:-}" # Color output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Helper functions error() { echo -e "${RED}Error: $1${NC}" >&2 exit 1 } success() { echo -e "${GREEN}$1${NC}" } warn() { echo -e "${YELLOW}$1${NC}" } check_config() { [[ -z "$WORKSPACE_ID" ]] && error "MOSAIC_WORKSPACE_ID not set" [[ -z "$API_TOKEN" ]] && error "MOSAIC_API_TOKEN not set" } # API call helper api_call() { local method="$1" local endpoint="$2" local data="${3:-}" local url="${API_URL}${endpoint}" local args=( -X "$method" -H "Authorization: Bearer ${API_TOKEN}" -H "Content-Type: application/json" -H "x-workspace-id: ${WORKSPACE_ID}" -s ) if [[ -n "$data" ]]; then args+=(-d "$data") fi curl "${args[@]}" "$url" } # Commands cmd_capture() { local content="" local title="" while [[ $# -gt 0 ]]; do case $1 in --title) title="$2" shift 2 ;; *) content="$1" shift ;; esac done [[ -z "$content" ]] && error "Content required for capture" local payload payload=$(jq -n \ --arg content "$content" \ --arg title "$title" \ '{content: $content} + (if $title != "" then {title: $title} else {} end)') response=$(api_call POST "/api/ideas/capture" "$payload") echo "$response" | jq -r '.id // empty' > /dev/null || error "Failed to capture idea" local idea_id idea_id=$(echo "$response" | jq -r '.id') success "✓ Idea captured: $idea_id" echo "$response" | jq '.' } cmd_create() { local content="" local title="" local tags="" local category="" local status="" local priority="" while [[ $# -gt 0 ]]; do case $1 in --content) content="$2" shift 2 ;; --title) title="$2" shift 2 ;; --tags) tags="$2" shift 2 ;; --category) category="$2" shift 2 ;; --status) status="$2" shift 2 ;; --priority) priority="$2" shift 2 ;; *) content="$1" shift ;; esac done [[ -z "$content" ]] && error "Content required" # Build JSON payload local payload="{\"content\": \"$content\"" [[ -n "$title" ]] && payload+=", \"title\": \"$title\"" [[ -n "$category" ]] && payload+=", \"category\": \"$category\"" [[ -n "$status" ]] && payload+=", \"status\": \"$status\"" [[ -n "$priority" ]] && payload+=", \"priority\": \"$priority\"" if [[ -n "$tags" ]]; then local tags_json tags_json=$(echo "$tags" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') payload+=", \"tags\": $tags_json" fi payload+="}" response=$(api_call POST "/api/ideas" "$payload") echo "$response" | jq -r '.id // empty' > /dev/null || error "Failed to create idea" local idea_id idea_id=$(echo "$response" | jq -r '.id') success "✓ Idea created: $idea_id" echo "$response" | jq '.' } cmd_list() { local limit="20" local tags="" local status="" while [[ $# -gt 0 ]]; do case $1 in --limit) limit="$2" shift 2 ;; --tags) tags="$2" shift 2 ;; --status) status="$2" shift 2 ;; *) shift ;; esac done local query="?limit=$limit" [[ -n "$tags" ]] && query+="&tags=$tags" [[ -n "$status" ]] && query+="&status=$status" response=$(api_call GET "/api/ideas${query}") echo "$response" | jq '.' } cmd_get() { local idea_id="$1" [[ -z "$idea_id" ]] && error "Idea ID required" response=$(api_call GET "/api/ideas/${idea_id}") echo "$response" | jq '.' } cmd_update() { local idea_id="" local title="" local content="" local tags="" local add_tags="" local remove_tags="" local status="" local category="" idea_id="$1" shift [[ -z "$idea_id" ]] && error "Idea ID required" while [[ $# -gt 0 ]]; do case $1 in --title) title="$2" shift 2 ;; --content) content="$2" shift 2 ;; --tags) tags="$2" shift 2 ;; --add-tags) add_tags="$2" shift 2 ;; --remove-tags) remove_tags="$2" shift 2 ;; --status) status="$2" shift 2 ;; --category) category="$2" shift 2 ;; *) shift ;; esac done # Build update payload local payload="{" local first=true if [[ -n "$title" ]]; then payload+="\"title\": \"$title\"" first=false fi if [[ -n "$content" ]]; then [[ "$first" == false ]] && payload+=", " payload+="\"content\": \"$content\"" first=false fi if [[ -n "$status" ]]; then [[ "$first" == false ]] && payload+=", " payload+="\"status\": \"$status\"" first=false fi if [[ -n "$category" ]]; then [[ "$first" == false ]] && payload+=", " payload+="\"category\": \"$category\"" first=false fi if [[ -n "$tags" ]]; then [[ "$first" == false ]] && payload+=", " local tags_json tags_json=$(echo "$tags" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') payload+="\"tags\": $tags_json" first=false fi payload+="}" response=$(api_call PATCH "/api/ideas/${idea_id}" "$payload") echo "$response" | jq -r '.id // empty' > /dev/null || error "Failed to update idea" success "✓ Idea updated: $idea_id" echo "$response" | jq '.' } cmd_delete() { local idea_id="$1" [[ -z "$idea_id" ]] && error "Idea ID required" response=$(api_call DELETE "/api/ideas/${idea_id}") success "✓ Idea deleted: $idea_id" } cmd_query() { local query="$*" [[ -z "$query" ]] && error "Query text required" local payload payload=$(jq -n --arg query "$query" '{query: $query}') response=$(api_call POST "/api/brain/query" "$payload") echo "$response" | jq '.' } cmd_search() { local search_term="" local limit="20" while [[ $# -gt 0 ]]; do case $1 in --limit) limit="$2" shift 2 ;; *) search_term="$1" shift ;; esac done [[ -z "$search_term" ]] && error "Search term required" local query="?q=$(echo "$search_term" | jq -sRr @uri)&limit=$limit" response=$(api_call GET "/api/brain/search${query}") echo "$response" | jq '.' } cmd_tags() { # Get all ideas and extract unique tags response=$(api_call GET "/api/ideas?limit=1000") echo "$response" | jq -r '.data[]?.tags[]? // empty' | sort -u } # Main main() { [[ $# -eq 0 ]] && error "Command required. Use: capture, create, list, get, update, delete, query, search, tags" check_config local command="$1" shift case "$command" in capture) cmd_capture "$@" ;; create) cmd_create "$@" ;; list) cmd_list "$@" ;; get) cmd_get "$@" ;; update) cmd_update "$@" ;; delete) cmd_delete "$@" ;; query) cmd_query "$@" ;; search) cmd_search "$@" ;; tags) cmd_tags "$@" ;; *) error "Unknown command: $command" ;; esac } main "$@"