893 lines
30 KiB
Bash
Executable File
893 lines
30 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
# ============================================================================
|
||
# Clawdbot Memory System — One-Command Installer v2.0
|
||
# https://github.com/BusyBee3333/clawdbot-memory-system
|
||
#
|
||
# Usage:
|
||
# bash install.sh # Interactive install
|
||
# bash install.sh --dry-run # Preview changes without applying
|
||
# bash install.sh --uninstall # Remove memory system config
|
||
# ============================================================================
|
||
|
||
VERSION="2.0.0"
|
||
|
||
# --- Colors & Formatting ---
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
MAGENTA='\033[0;35m'
|
||
CYAN='\033[0;36m'
|
||
BOLD='\033[1m'
|
||
DIM='\033[2m'
|
||
NC='\033[0m'
|
||
|
||
# --- State ---
|
||
DRY_RUN=false
|
||
UNINSTALL=false
|
||
CLAWDBOT_DIR=""
|
||
WORKSPACE_DIR=""
|
||
CONFIG_FILE=""
|
||
AGENTS_FILE=""
|
||
MEMORY_DIR=""
|
||
PROVIDER=""
|
||
API_KEY=""
|
||
INSTALL_ORG_SYSTEM=false
|
||
INSTALL_AUTO_SCAFFOLD=false
|
||
INSTALL_GIT_BACKUP=false
|
||
CHANGES_MADE=()
|
||
FILES_INDEXED=0
|
||
EXISTING_MEMORY_FILES=0
|
||
|
||
# --- Parse Args ---
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--dry-run) DRY_RUN=true ;;
|
||
--uninstall) UNINSTALL=true ;;
|
||
--help|-h)
|
||
echo "Clawdbot Memory System Installer v${VERSION}"
|
||
echo ""
|
||
echo "Usage:"
|
||
echo " bash install.sh Interactive install"
|
||
echo " bash install.sh --dry-run Preview changes without applying"
|
||
echo " bash install.sh --uninstall Remove memory system config"
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo -e "${RED}Unknown option: $arg${NC}"
|
||
echo "Use --help for usage information."
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# --- Helper Functions ---
|
||
info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
||
success() { echo -e "${GREEN}✅${NC} $1"; }
|
||
warn() { echo -e "${YELLOW}⚠️${NC} $1"; }
|
||
error() { echo -e "${RED}❌${NC} $1"; }
|
||
step() { echo -e "\n${BOLD}${MAGENTA}$1${NC}"; }
|
||
detail() { echo -e " ${DIM}$1${NC}"; }
|
||
dry() { echo -e " ${CYAN}[dry-run]${NC} $1"; }
|
||
jake() { echo -e " ${YELLOW}⭐ Jake's choice${NC}"; }
|
||
|
||
banner() {
|
||
echo ""
|
||
echo -e "${BOLD}${MAGENTA}"
|
||
echo " ╔══════════════════════════════════════════════╗"
|
||
echo " ║ 🧠 Clawdbot Memory System v${VERSION} ║"
|
||
echo " ║ Never lose context to compaction again ║"
|
||
echo " ╚══════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
}
|
||
|
||
# --- Detect Clawdbot Installation ---
|
||
detect_clawdbot() {
|
||
step "STEP 1/9 — 🔍 Detecting Clawdbot installation..."
|
||
|
||
for dir in "$HOME/.clawdbot" "$HOME/.openclaw" "$HOME/.moltbot"; do
|
||
if [[ -d "$dir" ]]; then
|
||
CLAWDBOT_DIR="$dir"
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ -z "$CLAWDBOT_DIR" ]]; then
|
||
error "Clawdbot installation not found!"
|
||
echo ""
|
||
echo " Looked in: ~/.clawdbot, ~/.openclaw, ~/.moltbot"
|
||
echo " Make sure Clawdbot is installed first: https://docs.clawd.bot/getting-started"
|
||
exit 1
|
||
fi
|
||
|
||
success "Found Clawdbot at ${BOLD}${CLAWDBOT_DIR}${NC}"
|
||
|
||
CONFIG_FILE="${CLAWDBOT_DIR}/clawdbot.json"
|
||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||
for name in "openclaw.json" "moltbot.json"; do
|
||
if [[ -f "${CLAWDBOT_DIR}/${name}" ]]; then
|
||
CONFIG_FILE="${CLAWDBOT_DIR}/${name}"
|
||
break
|
||
fi
|
||
done
|
||
fi
|
||
|
||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||
error "Config file not found at ${CONFIG_FILE}"
|
||
echo " Run 'clawdbot doctor' first to initialize your setup."
|
||
exit 1
|
||
fi
|
||
|
||
success "Found config at ${BOLD}${CONFIG_FILE}${NC}"
|
||
|
||
WORKSPACE_DIR=$(python3 -c "
|
||
import json, os
|
||
with open('${CONFIG_FILE}') as f:
|
||
cfg = json.load(f)
|
||
ws = cfg.get('agents', {}).get('defaults', {}).get('workspace', '')
|
||
if not ws:
|
||
ws = os.path.expanduser('~') + '/' + os.path.basename('${CLAWDBOT_DIR}') + '/workspace'
|
||
print(os.path.expanduser(ws))
|
||
" 2>/dev/null || echo "${CLAWDBOT_DIR}/workspace")
|
||
|
||
if [[ ! -d "$WORKSPACE_DIR" ]]; then
|
||
warn "Workspace directory not found at ${WORKSPACE_DIR}"
|
||
if $DRY_RUN; then
|
||
dry "Would create workspace directory"
|
||
else
|
||
mkdir -p "$WORKSPACE_DIR"
|
||
success "Created workspace directory"
|
||
fi
|
||
fi
|
||
|
||
success "Workspace: ${BOLD}${WORKSPACE_DIR}${NC}"
|
||
MEMORY_DIR="${WORKSPACE_DIR}/memory"
|
||
AGENTS_FILE="${WORKSPACE_DIR}/AGENTS.md"
|
||
}
|
||
|
||
# --- Check Dependencies ---
|
||
check_deps() {
|
||
step "STEP 2/9 — 🔧 Checking dependencies..."
|
||
|
||
if ! command -v jq &>/dev/null; then
|
||
warn "jq is not installed (needed for safe JSON config patching)"
|
||
if command -v brew &>/dev/null; then
|
||
read -rp " Install with Homebrew? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ "$yn" =~ ^[Yy] ]]; then
|
||
if $DRY_RUN; then
|
||
dry "Would run: brew install jq"
|
||
else
|
||
brew install jq
|
||
success "jq installed"
|
||
fi
|
||
else
|
||
error "jq is required. Install: brew install jq"
|
||
exit 1
|
||
fi
|
||
else
|
||
error "jq required: https://jqlang.github.io/jq/download/"
|
||
exit 1
|
||
fi
|
||
else
|
||
success "jq found: $(jq --version)"
|
||
fi
|
||
|
||
if ! command -v clawdbot &>/dev/null; then
|
||
if command -v openclaw &>/dev/null; then
|
||
alias clawdbot=openclaw
|
||
success "openclaw CLI found"
|
||
elif command -v moltbot &>/dev/null; then
|
||
alias clawdbot=moltbot
|
||
success "moltbot CLI found"
|
||
else
|
||
warn "clawdbot CLI not found — index build will be skipped"
|
||
fi
|
||
else
|
||
success "clawdbot CLI found"
|
||
fi
|
||
}
|
||
|
||
# --- Check Existing Memory Files ---
|
||
check_existing() {
|
||
step "STEP 3/9 — 📂 Checking existing memory files..."
|
||
|
||
if [[ -d "$MEMORY_DIR" ]]; then
|
||
EXISTING_MEMORY_FILES=$(find "$MEMORY_DIR" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
||
if [[ "$EXISTING_MEMORY_FILES" -gt 0 ]]; then
|
||
info "Found ${BOLD}${EXISTING_MEMORY_FILES}${NC} existing memory files — they'll be preserved and indexed"
|
||
echo ""
|
||
echo -e " ${DIM}Existing files:${NC}"
|
||
find "$MEMORY_DIR" -name "*.md" -type f 2>/dev/null | while read -r f; do
|
||
echo -e " ${DIM}$(basename "$f")${NC}"
|
||
done
|
||
else
|
||
info "Memory directory exists but is empty"
|
||
fi
|
||
else
|
||
info "No existing memory directory — will create one"
|
||
fi
|
||
|
||
if [[ -f "${WORKSPACE_DIR}/MEMORY.md" ]]; then
|
||
info "Found existing MEMORY.md — will be included in index"
|
||
fi
|
||
}
|
||
|
||
# --- Core Memory System Confirmation ---
|
||
confirm_core() {
|
||
step "STEP 4/9 — 🧠 Core Memory System"
|
||
|
||
echo ""
|
||
echo -e " ${BOLD}This installs:${NC}"
|
||
echo -e " ${GREEN}•${NC} Daily log system — agent writes ${CYAN}memory/YYYY-MM-DD.md${NC} each session"
|
||
echo -e " capturing decisions, preferences, project progress, and blockers"
|
||
echo -e " ${GREEN}•${NC} SQLite vector search index — semantic search over all memory files"
|
||
echo -e " (finds context even when wording differs, <100ms)"
|
||
echo -e " ${GREEN}•${NC} Hybrid search — 70% vector similarity + 30% keyword matching"
|
||
echo -e " ${GREEN}•${NC} Pre-compaction auto-flush — auto-saves context BEFORE session"
|
||
echo -e " compacts, so your agent never gets amnesia"
|
||
echo -e " ${GREEN}•${NC} Agent instructions — tells your agent when and how to use memory"
|
||
echo -e " ${GREEN}•${NC} Config patch — adds memorySearch settings without touching your"
|
||
echo -e " existing config (backs up first)"
|
||
echo ""
|
||
echo -e " ${DIM}Cost: ~\$0.50/month (OpenAI) or free (Gemini free tier / local)${NC}"
|
||
echo ""
|
||
jake
|
||
echo ""
|
||
read -rp " Install core memory system? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ ! "$yn" =~ ^[Yy] ]]; then
|
||
error "Core memory system is required. Exiting."
|
||
exit 0
|
||
fi
|
||
success "Core memory system will be installed"
|
||
}
|
||
|
||
# --- Choose Embedding Provider ---
|
||
choose_provider() {
|
||
step "STEP 5/9 — 🤖 Choose your embedding provider"
|
||
|
||
echo ""
|
||
echo -e " How should your agent generate search embeddings?"
|
||
echo ""
|
||
echo -e " ${BOLD}1)${NC} ${GREEN}OpenAI${NC} ${DIM}— fast, cheap (~\$0.02/M tokens), best quality${NC}"
|
||
jake
|
||
echo ""
|
||
echo -e " ${BOLD}2)${NC} ${BLUE}Gemini${NC} ${DIM}— free tier available, good quality${NC}"
|
||
echo ""
|
||
echo -e " ${BOLD}3)${NC} ${YELLOW}Local${NC} ${DIM}— fully offline, no API key, slower first build${NC}"
|
||
echo ""
|
||
|
||
read -rp " Choose [1/2/3] (default: 1): " choice
|
||
choice=${choice:-1}
|
||
|
||
case "$choice" in
|
||
1) PROVIDER="openai" ;;
|
||
2) PROVIDER="gemini" ;;
|
||
3) PROVIDER="local" ;;
|
||
*)
|
||
warn "Invalid choice, defaulting to OpenAI"
|
||
PROVIDER="openai"
|
||
;;
|
||
esac
|
||
|
||
success "Selected: ${BOLD}${PROVIDER}${NC}"
|
||
|
||
if [[ "$PROVIDER" == "openai" ]]; then
|
||
if [[ -n "${OPENAI_API_KEY:-}" ]]; then
|
||
info "Found OPENAI_API_KEY in environment"
|
||
else
|
||
echo ""
|
||
echo -e " ${DIM}OpenAI API key is needed for embeddings.${NC}"
|
||
echo -e " ${DIM}Get one at: https://platform.openai.com/api-keys${NC}"
|
||
echo -e " ${DIM}(Press Enter to skip — you can set OPENAI_API_KEY env var later)${NC}"
|
||
echo ""
|
||
read -rsp " OpenAI API Key: " API_KEY
|
||
echo ""
|
||
[[ -n "$API_KEY" ]] && success "API key provided" || warn "No key — set OPENAI_API_KEY before using memory search"
|
||
fi
|
||
elif [[ "$PROVIDER" == "gemini" ]]; then
|
||
if [[ -n "${GEMINI_API_KEY:-}" ]]; then
|
||
info "Found GEMINI_API_KEY in environment"
|
||
else
|
||
echo ""
|
||
echo -e " ${DIM}Gemini API key is needed for embeddings.${NC}"
|
||
echo -e " ${DIM}Get one at: https://aistudio.google.com/apikey${NC}"
|
||
echo -e " ${DIM}(Press Enter to skip — you can set GEMINI_API_KEY env var later)${NC}"
|
||
echo ""
|
||
read -rsp " Gemini API Key: " API_KEY
|
||
echo ""
|
||
[[ -n "$API_KEY" ]] && success "API key provided" || warn "No key — set GEMINI_API_KEY before using memory search"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# --- Organization System (Optional) ---
|
||
offer_org_system() {
|
||
step "STEP 6/9 — 📁 Organization System (optional add-on)"
|
||
|
||
echo ""
|
||
echo -e " ${BOLD}This adds a project organization framework on top of memory:${NC}"
|
||
echo ""
|
||
echo -e " ${GREEN}📁 Project Quickstart${NC}"
|
||
echo -e " When you start any new project, your agent automatically creates"
|
||
echo -e " structured tracking files with consistent naming so everything"
|
||
echo -e " is searchable and organized from day one."
|
||
echo ""
|
||
echo -e " ${GREEN}🔬 Research Intel System${NC}"
|
||
echo -e " For ongoing monitoring (competitors, markets, trends). Current week's"
|
||
echo -e " intel stays detailed at top, older weeks auto-compress to summaries."
|
||
echo -e " Agent checks these before making strategic recommendations."
|
||
echo ""
|
||
echo -e " ${GREEN}📊 Project Tracking${NC}"
|
||
echo -e " Standardized stages, blockers, decisions, and handoffs. Never lose"
|
||
echo -e " track of where a project stands across sessions."
|
||
echo ""
|
||
echo -e " ${GREEN}🏷️ Tag Convention${NC}"
|
||
echo -e " Consistent file naming (${CYAN}{project}-progress.md${NC},"
|
||
echo -e " ${CYAN}{project}-research-intel.md${NC}) so memory search groups"
|
||
echo -e " everything by project automatically."
|
||
echo ""
|
||
jake
|
||
echo ""
|
||
read -rp " Install the organization system? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ "$yn" =~ ^[Yy] ]]; then
|
||
INSTALL_ORG_SYSTEM=true
|
||
success "Organization system will be installed"
|
||
else
|
||
info "Skipping organization system (you can add it later by re-running the installer)"
|
||
fi
|
||
}
|
||
|
||
# --- Auto-Scaffold for New Projects ---
|
||
offer_auto_scaffold() {
|
||
if ! $INSTALL_ORG_SYSTEM; then return; fi
|
||
|
||
step "STEP 7/9 — 🚀 New Project Auto-Scaffold"
|
||
|
||
echo ""
|
||
echo -e " ${BOLD}When enabled, your agent auto-creates files for new projects.${NC}"
|
||
echo ""
|
||
echo -e " Example: You say ${DIM}\"Let's start a competitor analysis for Acme Corp\"${NC}"
|
||
echo -e " Agent automatically creates:"
|
||
echo -e " ${CYAN}memory/acme-corp-progress.md${NC} ${DIM}(project tracking)${NC}"
|
||
echo -e " ${CYAN}memory/acme-corp-research-intel.md${NC} ${DIM}(intel rotation)${NC}"
|
||
echo ""
|
||
echo -e " Your agent will ask which templates apply before creating:"
|
||
echo -e " • Progress tracker? ${DIM}(almost always yes)${NC}"
|
||
echo -e " • Research intel? ${DIM}(if monitoring/research is involved)${NC}"
|
||
echo -e " • Contacts tracker? ${DIM}(if other people are involved)${NC}"
|
||
echo ""
|
||
jake
|
||
echo ""
|
||
read -rp " Enable auto-scaffold for new projects? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ "$yn" =~ ^[Yy] ]]; then
|
||
INSTALL_AUTO_SCAFFOLD=true
|
||
success "Auto-scaffold enabled"
|
||
else
|
||
info "Skipping auto-scaffold"
|
||
fi
|
||
}
|
||
|
||
# --- Git Backup ---
|
||
offer_git_backup() {
|
||
step "STEP 8/9 — 💾 Git Backup"
|
||
|
||
echo ""
|
||
echo -e " ${BOLD}Back up memory files to git for version history and offsite safety.${NC}"
|
||
echo ""
|
||
echo -e " Your agent will be instructed to run:"
|
||
echo -e " ${DIM}cd ~/.clawdbot/workspace && git add -A && git commit -m \"backup\" && git push${NC}"
|
||
echo -e " at the end of each session or major milestone."
|
||
echo ""
|
||
echo -e " ${DIM}If your workspace isn't a git repo yet, you'll need to:${NC}"
|
||
echo -e " ${DIM} cd ~/.clawdbot/workspace && git init && git remote add origin <your-repo-url>${NC}"
|
||
echo ""
|
||
jake
|
||
echo ""
|
||
read -rp " Include git backup instructions? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ "$yn" =~ ^[Yy] ]]; then
|
||
INSTALL_GIT_BACKUP=true
|
||
success "Git backup instructions will be included"
|
||
else
|
||
info "Skipping git backup"
|
||
fi
|
||
}
|
||
|
||
# --- Setup Memory Directory & Templates ---
|
||
setup_memory_dir() {
|
||
step "STEP 9/9 — 📝 Installing..."
|
||
|
||
echo ""
|
||
info "Setting up memory directory and templates..."
|
||
|
||
if $DRY_RUN; then
|
||
[[ ! -d "$MEMORY_DIR" ]] && dry "Would create ${MEMORY_DIR}/"
|
||
dry "Would copy template files"
|
||
$INSTALL_ORG_SYSTEM && dry "Would copy organization templates"
|
||
return
|
||
fi
|
||
|
||
# Create memory directory
|
||
if [[ ! -d "$MEMORY_DIR" ]]; then
|
||
mkdir -p "$MEMORY_DIR"
|
||
success "Created ${MEMORY_DIR}/"
|
||
CHANGES_MADE+=("Created memory/ directory")
|
||
fi
|
||
|
||
# Determine template source
|
||
local script_dir
|
||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
local templates_dir="${script_dir}/templates"
|
||
local base_url="https://raw.githubusercontent.com/BusyBee3333/clawdbot-memory-system/main/templates"
|
||
|
||
# If running from curl (no local templates dir), download them
|
||
if [[ ! -d "$templates_dir" ]]; then
|
||
templates_dir=$(mktemp -d)
|
||
for tmpl in TEMPLATE-daily.md TEMPLATE-research-intel.md TEMPLATE-project-tracking.md TEMPLATE-contacts.md; do
|
||
curl -sL "${base_url}/${tmpl}" -o "${templates_dir}/${tmpl}" 2>/dev/null || true
|
||
done
|
||
fi
|
||
|
||
# Always copy daily template
|
||
if [[ -f "${templates_dir}/TEMPLATE-daily.md" ]] && [[ ! -f "${MEMORY_DIR}/TEMPLATE-daily.md" ]]; then
|
||
cp "${templates_dir}/TEMPLATE-daily.md" "${MEMORY_DIR}/TEMPLATE-daily.md"
|
||
success "Added template: TEMPLATE-daily.md"
|
||
CHANGES_MADE+=("Added TEMPLATE-daily.md")
|
||
fi
|
||
|
||
# Copy org system templates if selected
|
||
if $INSTALL_ORG_SYSTEM; then
|
||
for tmpl in TEMPLATE-research-intel.md TEMPLATE-project-tracking.md TEMPLATE-contacts.md; do
|
||
if [[ -f "${templates_dir}/${tmpl}" ]] && [[ ! -f "${MEMORY_DIR}/${tmpl}" ]]; then
|
||
cp "${templates_dir}/${tmpl}" "${MEMORY_DIR}/${tmpl}"
|
||
success "Added template: ${tmpl}"
|
||
CHANGES_MADE+=("Added ${tmpl}")
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Create today's daily log
|
||
local today
|
||
today=$(date +%Y-%m-%d)
|
||
if [[ ! -f "${MEMORY_DIR}/${today}.md" ]]; then
|
||
cat > "${MEMORY_DIR}/${today}.md" << EOF
|
||
# Daily Log — ${today}
|
||
|
||
## What We Worked On
|
||
- Installed Clawdbot Memory System 🧠
|
||
|
||
## Decisions Made
|
||
- Chose ${PROVIDER} for embeddings
|
||
$(${INSTALL_ORG_SYSTEM} && echo "- Enabled organization system with project scaffolding" || true)
|
||
$(${INSTALL_GIT_BACKUP} && echo "- Set up git backup for memory files" || true)
|
||
|
||
## Next Steps
|
||
- Start using the memory system naturally — agent writes daily logs
|
||
- Check memory/ directory for accumulated context over time
|
||
|
||
## Open Questions / Blockers
|
||
- (none)
|
||
|
||
## Notable Context
|
||
Memory system installed and indexed. Agent will now persist important context
|
||
across sessions and search it automatically before answering questions.
|
||
EOF
|
||
success "Created today's daily log: ${today}.md"
|
||
CHANGES_MADE+=("Created daily log ${today}.md")
|
||
fi
|
||
}
|
||
|
||
# --- Patch clawdbot.json ---
|
||
patch_config() {
|
||
info "Patching clawdbot.json..."
|
||
|
||
local existing_provider
|
||
existing_provider=$(jq -r '.agents.defaults.memorySearch.provider // empty' "$CONFIG_FILE" 2>/dev/null || true)
|
||
|
||
if [[ -n "$existing_provider" ]]; then
|
||
info "memorySearch already configured (provider: ${existing_provider})"
|
||
read -rp " Overwrite with new provider ($PROVIDER)? [y/N] " yn
|
||
yn=${yn:-N}
|
||
if [[ ! "$yn" =~ ^[Yy] ]]; then
|
||
info "Keeping existing config"
|
||
return
|
||
fi
|
||
fi
|
||
|
||
if $DRY_RUN; then
|
||
dry "Would patch clawdbot.json with memorySearch (${PROVIDER})"
|
||
return
|
||
fi
|
||
|
||
cp "$CONFIG_FILE" "${CONFIG_FILE}.pre-memory-backup"
|
||
success "Backed up config → clawdbot.json.pre-memory-backup"
|
||
|
||
local memory_config
|
||
case "$PROVIDER" in
|
||
openai)
|
||
memory_config='{"provider":"openai","model":"text-embedding-3-small","query":{"hybrid":{"enabled":true,"vectorWeight":0.7,"textWeight":0.3,"candidateMultiplier":4}}}'
|
||
[[ -n "$API_KEY" ]] && memory_config=$(echo "$memory_config" | jq --arg key "$API_KEY" '. + {remote: {apiKey: $key}}')
|
||
;;
|
||
gemini)
|
||
memory_config='{"provider":"gemini","model":"gemini-embedding-001"}'
|
||
[[ -n "$API_KEY" ]] && memory_config=$(echo "$memory_config" | jq --arg key "$API_KEY" '. + {remote: {apiKey: $key}}')
|
||
;;
|
||
local)
|
||
memory_config='{"provider":"local","fallback":"none"}'
|
||
;;
|
||
esac
|
||
|
||
local tmp_file
|
||
tmp_file=$(mktemp)
|
||
jq --argjson ms "$memory_config" '.agents.defaults.memorySearch = ((.agents.defaults.memorySearch // {}) * $ms)' "$CONFIG_FILE" > "$tmp_file"
|
||
|
||
if jq empty "$tmp_file" 2>/dev/null; then
|
||
mv "$tmp_file" "$CONFIG_FILE"
|
||
success "Added memorySearch config (provider: ${PROVIDER})"
|
||
CHANGES_MADE+=("Patched clawdbot.json (${PROVIDER} embeddings)")
|
||
else
|
||
rm -f "$tmp_file"
|
||
error "Failed to patch config — restoring backup"
|
||
cp "${CONFIG_FILE}.pre-memory-backup" "$CONFIG_FILE"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# --- Patch AGENTS.md ---
|
||
patch_agents() {
|
||
info "Updating AGENTS.md..."
|
||
|
||
if [[ -f "$AGENTS_FILE" ]] && grep -q "Memory System (auto-added by clawdbot-memory-system" "$AGENTS_FILE" 2>/dev/null; then
|
||
info "AGENTS.md already contains memory instructions — updating"
|
||
# Remove old section before re-adding
|
||
python3 -c "
|
||
import re
|
||
with open('${AGENTS_FILE}') as f:
|
||
content = f.read()
|
||
content = re.sub(r'\n*## Memory System \(auto-added by clawdbot-memory-system.*?\n(?=## [^M]|\Z)', '\n', content, flags=re.DOTALL)
|
||
with open('${AGENTS_FILE}', 'w') as f:
|
||
f.write(content.rstrip() + '\n')
|
||
" 2>/dev/null || true
|
||
fi
|
||
|
||
if $DRY_RUN; then
|
||
dry "Would append memory + org instructions to AGENTS.md"
|
||
return
|
||
fi
|
||
|
||
# Build the patch content
|
||
local patch=""
|
||
patch+="
|
||
## Memory System (auto-added by clawdbot-memory-system installer v${VERSION})
|
||
|
||
### Mandatory Memory Recall
|
||
Before answering ANY question about prior work, decisions, dates, people, preferences, or context from previous sessions:
|
||
1. Run \`memory_search\` with a relevant query
|
||
2. Use \`memory_get\` to pull specific lines if needed
|
||
3. Check today's daily log: \`memory/YYYY-MM-DD.md\`
|
||
4. Check yesterday's log if today's is sparse
|
||
5. Only say \"I don't recall\" if memory search returns nothing
|
||
|
||
### Daily Memory Log
|
||
- Write to \`memory/YYYY-MM-DD.md\` throughout the session
|
||
- Log: decisions made, user preferences discovered, project progress, action items, blockers
|
||
- Be specific — future-you needs exact details, not vague summaries
|
||
- Include: names, URLs, version numbers, error messages, config values — anything painful to re-discover
|
||
- On session start, read today + yesterday's logs to restore context
|
||
|
||
### Pre-Compaction Flush
|
||
When you sense a session is getting long or receive a compaction warning:
|
||
- Write ALL important unsaved context to today's daily log immediately
|
||
- Include: what we were working on, where we left off, any pending decisions, partial results
|
||
- This is your last chance before amnesia — be thorough, not brief"
|
||
|
||
if $INSTALL_ORG_SYSTEM; then
|
||
patch+="
|
||
|
||
### Organization System — Project Files
|
||
When starting any new project or workstream, create structured tracking files:
|
||
|
||
**File naming convention:** \`memory/{project-slug}-{type}.md\`
|
||
- \`{project-slug}\`: lowercase, hyphenated (e.g., \`acme-analysis\`, \`competitor-intel\`)
|
||
- \`{type}\`: one of \`progress\`, \`research-intel\`, \`contacts\`
|
||
|
||
**Templates available in memory/ directory:**
|
||
- \`TEMPLATE-project-tracking.md\` — stages, blockers, decisions, next steps
|
||
- \`TEMPLATE-research-intel.md\` — weekly intel rotation with auto-compression
|
||
- \`TEMPLATE-contacts.md\` — people, roles, access levels"
|
||
fi
|
||
|
||
if $INSTALL_AUTO_SCAFFOLD; then
|
||
patch+="
|
||
|
||
### Auto-Scaffold for New Projects
|
||
When the user says anything like \"start a new project\", \"let's work on X\", or \"begin tracking Y\":
|
||
1. Ask which templates apply:
|
||
- Progress tracker? (almost always yes)
|
||
- Research intel? (if monitoring/research is involved)
|
||
- Contacts tracker? (if other people are involved)
|
||
2. Create the files using the naming convention: \`memory/{project-slug}-{type}.md\`
|
||
3. Copy from the templates in memory/ directory
|
||
4. Replace {placeholders} with actual project details
|
||
5. Add the project to today's daily log under \"What We Worked On\"
|
||
6. Confirm what was created and what each file is for"
|
||
fi
|
||
|
||
patch+="
|
||
|
||
### Research Intel System
|
||
For ongoing research/monitoring projects:
|
||
- Store in: \`memory/{project}-research-intel.md\`
|
||
- Current week's detailed intel at TOP of file
|
||
- Compressed 1-3 sentence summaries of previous weeks at BOTTOM
|
||
- Weekly: compress last week to summary, add new detailed intel at top
|
||
- When asked about action items or strategy, check active research intel files first"
|
||
|
||
if $INSTALL_GIT_BACKUP; then
|
||
patch+="
|
||
|
||
### Git Backup Habit
|
||
End of each session or major milestone:
|
||
\`\`\`bash
|
||
cd ~/.clawdbot/workspace && git add -A && git commit -m \"session backup: \$(date +%Y-%m-%d)\" && git push
|
||
\`\`\`
|
||
This keeps identity, memory, and progress backed up offsite. Remind the user if they haven't backed up recently."
|
||
fi
|
||
|
||
if [[ -f "$AGENTS_FILE" ]]; then
|
||
echo "" >> "$AGENTS_FILE"
|
||
echo "$patch" >> "$AGENTS_FILE"
|
||
else
|
||
echo "$patch" > "$AGENTS_FILE"
|
||
fi
|
||
|
||
success "Updated AGENTS.md with memory instructions"
|
||
$INSTALL_ORG_SYSTEM && success "Added organization system to AGENTS.md"
|
||
$INSTALL_AUTO_SCAFFOLD && success "Added auto-scaffold instructions to AGENTS.md"
|
||
$INSTALL_GIT_BACKUP && success "Added git backup habit to AGENTS.md"
|
||
CHANGES_MADE+=("Updated AGENTS.md")
|
||
}
|
||
|
||
# --- Build Index ---
|
||
build_index() {
|
||
info "Building memory search index..."
|
||
|
||
if $DRY_RUN; then
|
||
dry "Would run: clawdbot memory index --verbose"
|
||
return
|
||
fi
|
||
|
||
if ! command -v clawdbot &>/dev/null; then
|
||
warn "clawdbot CLI not in PATH — skipping index build"
|
||
detail "Run manually: clawdbot memory index --verbose"
|
||
return
|
||
fi
|
||
|
||
echo -e "${DIM}"
|
||
if clawdbot memory index --verbose 2>&1; then
|
||
echo -e "${NC}"
|
||
success "Index built successfully"
|
||
else
|
||
echo -e "${NC}"
|
||
warn "Index build had issues — run 'clawdbot memory status --deep' to check"
|
||
fi
|
||
|
||
FILES_INDEXED=$(find "$MEMORY_DIR" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
||
[[ -f "${WORKSPACE_DIR}/MEMORY.md" ]] && FILES_INDEXED=$((FILES_INDEXED + 1))
|
||
}
|
||
|
||
# --- Review & Confirm ---
|
||
review_confirm() {
|
||
echo ""
|
||
echo -e "${BOLD}${CYAN}"
|
||
echo " ╔══════════════════════════════════════════════╗"
|
||
echo " ║ 📋 Review Before Installing ║"
|
||
echo " ╚══════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
echo ""
|
||
echo -e " ${BOLD}Will install:${NC}"
|
||
echo ""
|
||
echo -e " ${GREEN}✅${NC} Core Memory System"
|
||
echo -e " ${DIM}• memory/ directory with daily log template${NC}"
|
||
echo -e " ${DIM}• SQLite vector search (${PROVIDER} embeddings)${NC}"
|
||
echo -e " ${DIM}• Pre-compaction auto-flush${NC}"
|
||
echo -e " ${DIM}• Agent instructions in AGENTS.md${NC}"
|
||
|
||
if $INSTALL_ORG_SYSTEM; then
|
||
echo ""
|
||
echo -e " ${GREEN}✅${NC} Organization System"
|
||
echo -e " ${DIM}• Project tracking template${NC}"
|
||
echo -e " ${DIM}• Research intel template${NC}"
|
||
echo -e " ${DIM}• Contacts template${NC}"
|
||
echo -e " ${DIM}• Tag naming convention${NC}"
|
||
else
|
||
echo ""
|
||
echo -e " ${DIM}⏭️ Organization System (skipped)${NC}"
|
||
fi
|
||
|
||
if $INSTALL_AUTO_SCAFFOLD; then
|
||
echo ""
|
||
echo -e " ${GREEN}✅${NC} Auto-Scaffold for New Projects"
|
||
fi
|
||
|
||
if $INSTALL_GIT_BACKUP; then
|
||
echo ""
|
||
echo -e " ${GREEN}✅${NC} Git Backup Instructions"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e " ${BOLD}Config changes:${NC}"
|
||
echo -e " ${DIM}• clawdbot.json → adding memorySearch config${NC}"
|
||
echo -e " ${DIM}• AGENTS.md → appending memory instructions${NC}"
|
||
echo -e " ${DIM}• Backup: clawdbot.json.pre-memory-backup${NC}"
|
||
|
||
if [[ "$EXISTING_MEMORY_FILES" -gt 0 ]]; then
|
||
echo ""
|
||
echo -e " ${BOLD}Migration:${NC}"
|
||
echo -e " ${DIM}• ${EXISTING_MEMORY_FILES} existing memory files will be preserved and indexed${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
read -rp " Proceed with installation? [Y/n] " yn
|
||
yn=${yn:-Y}
|
||
if [[ ! "$yn" =~ ^[Yy] ]]; then
|
||
info "Installation cancelled"
|
||
exit 0
|
||
fi
|
||
}
|
||
|
||
# --- Print Summary ---
|
||
print_summary() {
|
||
echo ""
|
||
echo -e "${BOLD}${GREEN}"
|
||
echo " ╔══════════════════════════════════════════════╗"
|
||
echo " ║ 🎉 Installation Complete! ║"
|
||
echo " ╚══════════════════════════════════════════════╝"
|
||
echo -e "${NC}"
|
||
|
||
if [[ ${#CHANGES_MADE[@]} -gt 0 ]]; then
|
||
echo -e " ${BOLD}Changes made:${NC}"
|
||
for change in "${CHANGES_MADE[@]}"; do
|
||
echo -e " ${GREEN}✓${NC} ${change}"
|
||
done
|
||
echo ""
|
||
fi
|
||
|
||
if [[ "$EXISTING_MEMORY_FILES" -gt 0 ]]; then
|
||
echo -e " ${BOLD}Migration:${NC}"
|
||
echo -e " 📁 ${EXISTING_MEMORY_FILES} existing memory files preserved and indexed"
|
||
echo ""
|
||
fi
|
||
|
||
echo -e " ${BOLD}Index:${NC}"
|
||
echo -e " 📊 ${FILES_INDEXED} memory files indexed"
|
||
echo -e " 🤖 Provider: ${PROVIDER}"
|
||
echo ""
|
||
|
||
echo -e " ${BOLD}${CYAN}Next steps:${NC}"
|
||
echo ""
|
||
echo -e " ${BOLD}1.${NC} Restart the gateway to apply config changes:"
|
||
echo -e " ${DIM}clawdbot gateway restart${NC}"
|
||
echo ""
|
||
echo -e " ${BOLD}2.${NC} Start chatting! Your agent will now:"
|
||
echo -e " • Write daily logs to ${CYAN}memory/YYYY-MM-DD.md${NC}"
|
||
echo -e " • Search memories before answering about prior work"
|
||
echo -e " • Flush context before compaction (no more amnesia!)"
|
||
if $INSTALL_ORG_SYSTEM; then
|
||
echo -e " • Create structured files when you start new projects"
|
||
fi
|
||
echo ""
|
||
echo -e " ${BOLD}3.${NC} Test it! Ask your agent:"
|
||
echo -e " ${DIM}\"What did we work on today?\"${NC}"
|
||
if $INSTALL_AUTO_SCAFFOLD; then
|
||
echo -e " ${DIM}\"Start a new project called competitor analysis\"${NC}"
|
||
fi
|
||
echo ""
|
||
echo -e " ${BOLD}4.${NC} Verify anytime:"
|
||
echo -e " ${DIM}clawdbot memory status --deep${NC}"
|
||
echo ""
|
||
|
||
if [[ -n "$API_KEY" ]]; then
|
||
echo -e " ${YELLOW}⚠️ Your API key was saved to clawdbot.json.${NC}"
|
||
echo -e " ${DIM} Alternatively, set as env var and remove from config.${NC}"
|
||
echo ""
|
||
fi
|
||
|
||
echo -e " ${DIM}Your agent will NEVER forget again. ᕕ( ᐛ )ᕗ${NC}"
|
||
echo ""
|
||
echo -e " ${DIM}Problems? https://github.com/BusyBee3333/clawdbot-memory-system#troubleshooting${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# --- Uninstall ---
|
||
do_uninstall() {
|
||
banner
|
||
step "🗑️ Uninstalling Clawdbot Memory System..."
|
||
detect_clawdbot
|
||
|
||
echo ""
|
||
warn "This will:"
|
||
echo " • Remove memorySearch config from clawdbot.json"
|
||
echo " • Remove memory instructions from AGENTS.md"
|
||
echo ""
|
||
echo -e " ${BOLD}This will NOT delete your memory/ files.${NC}"
|
||
echo " Your memories are safe — only the config is removed."
|
||
echo ""
|
||
read -rp " Continue? [y/N] " yn
|
||
yn=${yn:-N}
|
||
[[ ! "$yn" =~ ^[Yy] ]] && { info "Cancelled"; exit 0; }
|
||
|
||
if [[ -f "$CONFIG_FILE" ]] && jq -e '.agents.defaults.memorySearch' "$CONFIG_FILE" &>/dev/null; then
|
||
cp "$CONFIG_FILE" "${CONFIG_FILE}.pre-uninstall-backup"
|
||
local tmp_file; tmp_file=$(mktemp)
|
||
jq 'del(.agents.defaults.memorySearch)' "$CONFIG_FILE" > "$tmp_file"
|
||
if jq empty "$tmp_file" 2>/dev/null; then
|
||
mv "$tmp_file" "$CONFIG_FILE"
|
||
success "Removed memorySearch from config"
|
||
else
|
||
rm -f "$tmp_file"; error "Failed to patch config"
|
||
fi
|
||
fi
|
||
|
||
if [[ -f "$AGENTS_FILE" ]] && grep -q "Memory System (auto-added by clawdbot-memory-system" "$AGENTS_FILE" 2>/dev/null; then
|
||
cp "$AGENTS_FILE" "${AGENTS_FILE}.pre-uninstall-backup"
|
||
python3 -c "
|
||
import re
|
||
with open('${AGENTS_FILE}') as f:
|
||
content = f.read()
|
||
content = re.sub(r'\n*## Memory System \(auto-added by clawdbot-memory-system.*', '', content, flags=re.DOTALL)
|
||
with open('${AGENTS_FILE}', 'w') as f:
|
||
f.write(content.rstrip() + '\n')
|
||
" 2>/dev/null || true
|
||
success "Removed memory instructions from AGENTS.md"
|
||
fi
|
||
|
||
echo ""
|
||
success "Uninstall complete"
|
||
echo -e " ${DIM}Memory files preserved. Run 'clawdbot gateway restart' to apply.${NC}"
|
||
exit 0
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main Flow
|
||
# ============================================================================
|
||
|
||
[[ "$UNINSTALL" == true ]] && do_uninstall
|
||
|
||
banner
|
||
|
||
$DRY_RUN && echo -e " ${CYAN}${BOLD}Running in dry-run mode — no changes will be made${NC}\n"
|
||
|
||
# Interactive onboarding
|
||
detect_clawdbot # Step 1
|
||
check_deps # Step 2
|
||
check_existing # Step 3
|
||
confirm_core # Step 4
|
||
choose_provider # Step 5
|
||
offer_org_system # Step 6
|
||
offer_auto_scaffold # Step 7
|
||
offer_git_backup # Step 8
|
||
review_confirm # Review
|
||
|
||
# Install
|
||
setup_memory_dir # Step 9 (creates dirs + templates)
|
||
patch_config # (patches clawdbot.json)
|
||
patch_agents # (patches AGENTS.md)
|
||
build_index # (builds SQLite index)
|
||
|
||
# Done
|
||
if $DRY_RUN; then
|
||
echo ""
|
||
echo -e " ${CYAN}${BOLD}Dry run complete — no changes were made.${NC}"
|
||
echo -e " ${DIM}Run without --dry-run to apply.${NC}"
|
||
echo ""
|
||
else
|
||
print_summary
|
||
fi
|