diff --git a/packages/skills/brain/.env.example b/packages/skills/brain/.env.example new file mode 100644 index 0000000..c6e2979 --- /dev/null +++ b/packages/skills/brain/.env.example @@ -0,0 +1,11 @@ +# Mosaic Brain Configuration Example +# Copy to ~/.config/mosaic/brain.conf and fill in your values + +# Mosaic Stack API URL +MOSAIC_API_URL=http://localhost:3001 + +# Your workspace UUID (get from Mosaic Stack web app) +MOSAIC_WORKSPACE_ID= + +# Your API authentication token (get from Mosaic Stack settings) +MOSAIC_API_TOKEN= diff --git a/packages/skills/brain/README.md b/packages/skills/brain/README.md new file mode 100644 index 0000000..b632946 --- /dev/null +++ b/packages/skills/brain/README.md @@ -0,0 +1,98 @@ +# Mosaic Brain Skill + +A Clawdbot skill for integrating with Mosaic Stack's Ideas/Brain API. + +## Features + +- **Quick Brain Dump**: Capture ideas rapidly with `capture` command +- **Semantic Search**: Query your knowledge base using natural language +- **Full CRUD**: Create, read, update, delete ideas with rich metadata +- **Tag Management**: Organize ideas with tags +- **Flexible Queries**: Filter by status, tags, dates, and more + +## Installation + +1. Copy this skill to your Clawdbot skills directory or link it: + ```bash + ln -s ~/src/mosaic-stack/packages/skills/brain ~/.config/clawdbot/skills/mosaic-brain + ``` + +2. Configure your Mosaic Stack connection: + ```bash + mkdir -p ~/.config/mosaic + cat > ~/.config/mosaic/brain.conf < --status "IN_PROGRESS" --add-tags "urgent" +``` + +## API Reference + +This skill interfaces with the following Mosaic Stack endpoints: + +- `POST /api/ideas/capture` - Quick capture +- `POST /api/ideas` - Create full idea +- `GET /api/ideas` - List ideas +- `GET /api/ideas/:id` - Get idea +- `PATCH /api/ideas/:id` - Update idea +- `DELETE /api/ideas/:id` - Delete idea +- `POST /api/brain/query` - Semantic query +- `GET /api/brain/search` - Keyword search + +## Development + +### Requirements + +- `curl` - HTTP client +- `jq` - JSON processor +- Mosaic Stack API running and accessible + +### Testing + +```bash +# Test capture +./brain.sh capture "Test idea $(date)" + +# Test search +./brain.sh search "test" + +# Test query +./brain.sh query "recent test ideas" +``` + +## License + +Part of the Mosaic Stack ecosystem. diff --git a/packages/skills/brain/SKILL.md b/packages/skills/brain/SKILL.md new file mode 100644 index 0000000..be01259 --- /dev/null +++ b/packages/skills/brain/SKILL.md @@ -0,0 +1,139 @@ +--- +name: mosaic-brain +description: Capture ideas, brain dumps, and semantic search across your Mosaic Stack knowledge base. +homepage: https://mosaicstack.dev +metadata: {"clawdbot":{"emoji":"🧠","requires":{"bins":["curl","jq"]}}} +--- + +# Mosaic Brain + +Capture and search ideas, brain dumps, and notes in your Mosaic Stack workspace. Provides quick capture, semantic search, and tag management. + +## Setup + +1. Ensure Mosaic Stack API is running (default: `http://localhost:3001`) +2. Set environment variables: + ```bash + export MOSAIC_API_URL="http://localhost:3001" + export MOSAIC_WORKSPACE_ID="your-workspace-uuid" + export MOSAIC_API_TOKEN="your-auth-token" + ``` + +3. Optionally create `~/.config/mosaic/brain.conf`: + ```bash + MOSAIC_API_URL=http://localhost:3001 + MOSAIC_WORKSPACE_ID=your-workspace-uuid + MOSAIC_API_TOKEN=your-auth-token + ``` + +## Common Commands + +### Quick Brain Dump +Capture an idea quickly without thinking about categorization: +```bash +# Quick capture +./brain.sh capture "Had a great idea about using AI for code reviews" + +# Capture with title +./brain.sh capture "Remember to follow up with the team about the new feature" --title "Team Follow-up" +``` + +### Create Detailed Idea +Create an idea with full metadata: +```bash +./brain.sh create \ + --title "New Feature Idea" \ + --content "Implement semantic search across all documents..." \ + --tags "feature,ai,search" \ + --category "product" +``` + +### Semantic Search +Search your brain using natural language: +```bash +# Query your knowledge base +./brain.sh query "What did I say about AI and code reviews?" + +# Search with limit +./brain.sh search "project ideas" --limit 10 +``` + +### List Ideas +```bash +# List recent ideas +./brain.sh list + +# List with filters +./brain.sh list --limit 20 --tags "work,urgent" +``` + +### Get Specific Idea +```bash +./brain.sh get +``` + +### Update Idea +```bash +./brain.sh update \ + --title "Updated Title" \ + --tags "new,tags" \ + --status "IN_PROGRESS" +``` + +### Delete Idea +```bash +./brain.sh delete +``` + +### Tag Management +```bash +# List all tags +./brain.sh tags + +# Add tags to an idea +./brain.sh update --add-tags "urgent,review" + +# Remove tags from an idea +./brain.sh update --remove-tags "old-tag" +``` + +## API Endpoints + +The skill uses these Mosaic Stack API endpoints: + +- **POST /api/ideas/capture** - Quick brain dump (minimal fields) +- **POST /api/ideas** - Create full idea with metadata +- **GET /api/ideas** - List ideas with filters +- **GET /api/ideas/:id** - Get specific idea +- **PATCH /api/ideas/:id** - Update idea +- **DELETE /api/ideas/:id** - Delete idea +- **POST /api/brain/query** - Semantic search/query +- **GET /api/brain/search** - Search ideas + +## Configuration + +Config file priority: +1. Environment variables +2. `~/.config/mosaic/brain.conf` +3. Default values + +Required settings: +- `MOSAIC_API_URL` - API base URL (default: http://localhost:3001) +- `MOSAIC_WORKSPACE_ID` - Your workspace UUID +- `MOSAIC_API_TOKEN` - Authentication token + +## Usage from Clawdbot + +Natural language examples: +- "Remember this..." / "Note to self..." → Quick capture +- "What did I say about..." → Semantic search +- "Show me my recent ideas" → List ideas +- "Brain dump: [content]" → Quick capture + +## Notes + +- Quick capture (`capture`) is fastest - just dump your thought +- Semantic search (`query`) uses AI to find relevant ideas +- Regular search is keyword-based, faster but less intelligent +- Tags are automatically indexed for quick filtering +- Ideas support markdown formatting in content diff --git a/packages/skills/brain/brain.sh b/packages/skills/brain/brain.sh new file mode 100755 index 0000000..cd84d4c --- /dev/null +++ b/packages/skills/brain/brain.sh @@ -0,0 +1,393 @@ +#!/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 + -w "\n%{http_code}" + ) + + if [[ -n "$data" ]]; then + args+=(-d "$data") + fi + + local response + response=$(curl "${args[@]}" "$url") + + # Extract HTTP status code from last line + local http_code + http_code=$(echo "$response" | tail -n1) + local body + body=$(echo "$response" | sed '$d') + + # Check for errors + if [[ "$http_code" -ge 400 ]]; then + local error_msg + error_msg=$(echo "$body" | jq -r '.message // .error // "API request failed"' 2>/dev/null || echo "API request failed") + error "HTTP $http_code: $error_msg" + fi + + echo "$body" +} + +# 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 using jq for safety + local payload + payload=$(jq -n \ + --arg content "$content" \ + --arg title "$title" \ + --arg category "$category" \ + --arg status "$status" \ + --arg priority "$priority" \ + --arg tags "$tags" \ + '{content: $content} + + (if $title != "" then {title: $title} else {} end) + + (if $category != "" then {category: $category} else {} end) + + (if $status != "" then {status: $status} else {} end) + + (if $priority != "" then {priority: $priority} else {} end) + + (if $tags != "" then {tags: ($tags | split(",") | map(gsub("^\\s+|\\s+$";"")))} else {} end)' + ) + + 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 using jq for safety + local payload + payload=$(jq -n \ + --arg title "$title" \ + --arg content "$content" \ + --arg status "$status" \ + --arg category "$category" \ + --arg tags "$tags" \ + '(if $title != "" then {title: $title} else {} end) + + (if $content != "" then {content: $content} else {} end) + + (if $status != "" then {status: $status} else {} end) + + (if $category != "" then {category: $category} else {} end) + + (if $tags != "" then {tags: ($tags | split(",") | map(gsub("^\\s+|\\s+$";"")))} else {} end)' + ) + + 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 "$@"