Release: CI/CD Pipeline & Architecture Updates #177
11
packages/skills/brain/.env.example
Normal file
11
packages/skills/brain/.env.example
Normal file
@@ -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=
|
||||
98
packages/skills/brain/README.md
Normal file
98
packages/skills/brain/README.md
Normal file
@@ -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 <<EOF
|
||||
MOSAIC_API_URL=http://localhost:3001
|
||||
MOSAIC_WORKSPACE_ID=your-workspace-uuid
|
||||
MOSAIC_API_TOKEN=your-auth-token
|
||||
EOF
|
||||
```
|
||||
|
||||
3. Verify setup:
|
||||
```bash
|
||||
./brain.sh list --limit 5
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
See [SKILL.md](./SKILL.md) for detailed usage documentation.
|
||||
|
||||
### Quick Examples
|
||||
|
||||
```bash
|
||||
# Capture a quick thought
|
||||
./brain.sh capture "Need to implement user authentication for the new app"
|
||||
|
||||
# Create detailed idea
|
||||
./brain.sh create \
|
||||
--title "Authentication System" \
|
||||
--content "Implement OAuth2 + JWT auth..." \
|
||||
--tags "backend,security,auth" \
|
||||
--priority "HIGH"
|
||||
|
||||
# Search semantically
|
||||
./brain.sh query "What did I say about authentication?"
|
||||
|
||||
# List recent ideas
|
||||
./brain.sh list --limit 10
|
||||
|
||||
# Update an idea
|
||||
./brain.sh update <id> --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.
|
||||
139
packages/skills/brain/SKILL.md
Normal file
139
packages/skills/brain/SKILL.md
Normal file
@@ -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 <idea-id>
|
||||
```
|
||||
|
||||
### Update Idea
|
||||
```bash
|
||||
./brain.sh update <idea-id> \
|
||||
--title "Updated Title" \
|
||||
--tags "new,tags" \
|
||||
--status "IN_PROGRESS"
|
||||
```
|
||||
|
||||
### Delete Idea
|
||||
```bash
|
||||
./brain.sh delete <idea-id>
|
||||
```
|
||||
|
||||
### Tag Management
|
||||
```bash
|
||||
# List all tags
|
||||
./brain.sh tags
|
||||
|
||||
# Add tags to an idea
|
||||
./brain.sh update <idea-id> --add-tags "urgent,review"
|
||||
|
||||
# Remove tags from an idea
|
||||
./brain.sh update <idea-id> --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
|
||||
393
packages/skills/brain/brain.sh
Executable file
393
packages/skills/brain/brain.sh
Executable file
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Mosaic Brain CLI - Interface to Mosaic Stack Ideas/Brain API
|
||||
# Usage: brain.sh <command> [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 "$@"
|
||||
Reference in New Issue
Block a user