/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 "🔧 Checking dependencies..." # Check for jq if ! command -v jq &>/dev/null; then warn "jq is not installed (needed for safe JSON config patching)" echo "" if command -v brew &>/dev/null; then echo -e " Install with Homebrew? ${DIM}(recommended)${NC}" read -rp " [Y/n] " yn yn=${yn:-Y} if [[ "$yn" =~ ^[Yy] ]]; then if $DRY_RUN; then dry "Would run: brew install jq" else echo " Installing jq..." brew install jq success "jq installed" fi else error "jq is required. Install it manually:" echo " brew install jq" echo " # or: sudo apt-get install jq" echo " # or: https://jqlang.github.io/jq/download/" exit 1 fi else error "jq is required but not installed." echo "" echo " Install jq:" echo " macOS: brew install jq" echo " Ubuntu/Debian: sudo apt-get install jq" echo " Other: https://jqlang.github.io/jq/download/" exit 1 fi else success "jq found: $(jq --version)" fi # Check for clawdbot CLI if ! command -v clawdbot &>/dev/null; then # Try openclaw or moltbot 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 in PATH" detail "Index building will be skipped — run manually after install" fi else success "clawdbot CLI found" fi } # --- Check Existing Memory Files --- check_existing() { step "📂 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 in ${MEMORY_DIR}" detail "These will be preserved and indexed" else info "Memory directory exists but is empty" fi else info "No existing memory directory — will create one" fi # Check for MEMORY.md if [[ -f "${WORKSPACE_DIR}/MEMORY.md" ]]; then info "Found existing MEMORY.md — will be included in index" fi } # --- Create Memory Directory & Templates --- setup_memory_dir() { step "📝 Setting up memory directory and templates..." if $DRY_RUN; then if [[ ! -d "$MEMORY_DIR" ]]; then dry "Would create ${MEMORY_DIR}/" fi dry "Would copy template files to ${MEMORY_DIR}/" return fi # Create memory directory if [[ ! -d "$MEMORY_DIR" ]]; then mkdir -p "$MEMORY_DIR" success "Created ${MEMORY_DIR}/" CHANGES_MADE+=("Created memory/ directory") else info "Memory directory already exists" fi # Copy templates (don't overwrite existing) local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local templates_dir="${script_dir}/templates" # If running from curl, download templates if [[ ! -d "$templates_dir" ]]; then local base_url="https://raw.githubusercontent.com/BusyBee3333/clawdbot-memory-system/main/templates" templates_dir=$(mktemp -d) for tmpl in TEMPLATE-daily.md TEMPLATE-research-intel.md TEMPLATE-project-tracking.md; do curl -sL "${base_url}/${tmpl}" -o "${templates_dir}/${tmpl}" 2>/dev/null || true done fi # Copy each template if it doesn't exist for tmpl_file in "${templates_dir}"/TEMPLATE-*.md; do [[ -f "$tmpl_file" ]] || continue local basename basename=$(basename "$tmpl_file") if [[ ! -f "${MEMORY_DIR}/${basename}" ]]; then cp "$tmpl_file" "${MEMORY_DIR}/${basename}" success "Added template: ${basename}" CHANGES_MADE+=("Added ${basename}") else detail "Template already exists: ${basename}" fi done # Create today's daily log if none exists local today today=$(date +%Y-%m-%d) if [[ ! -f "${MEMORY_DIR}/${today}.md" ]]; then cat > "${MEMORY_DIR}/${today}.md" << 'DAILY' # Daily Log — $(date +%Y-%m-%d) ## What We Worked On - Set up Clawdbot Memory System 🧠 ## Decisions Made - Installed persistent memory to prevent context loss during compaction ## Next Steps - Use the memory system naturally — agent will write daily logs - Check `memory/` directory for accumulated context over time ## Open Questions / Blockers - ## Notable Context Memory system installed and indexed. Agent should now persist important context across sessions automatically. DAILY # Fix the date in the file sed -i.bak "s/\$(date +%Y-%m-%d)/${today}/g" "${MEMORY_DIR}/${today}.md" 2>/dev/null && rm -f "${MEMORY_DIR}/${today}.md.bak" success "Created today's daily log: ${today}.md" CHANGES_MADE+=("Created daily log ${today}.md") fi } # --- Choose Embedding Provider --- choose_provider() { step "🤖 Choose your embedding provider..." echo "" echo -e " ${BOLD}1)${NC} ${GREEN}OpenAI${NC} ${DIM}(recommended — fast, cheap ~\$0.02/M tokens)${NC}" echo -e " ${BOLD}2)${NC} ${BLUE}Gemini${NC} ${DIM}(free tier available)${NC}" echo -e " ${BOLD}3)${NC} ${YELLOW}Local${NC} ${DIM}(free, offline, slower first run)${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}" # Prompt for API key if needed 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 it later as OPENAI_API_KEY)${NC}" echo "" read -rsp " OpenAI API Key: " API_KEY echo "" if [[ -n "$API_KEY" ]]; then success "API key provided" else warn "No API key provided — set OPENAI_API_KEY env var before using memory search" fi 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 it later as GEMINI_API_KEY)${NC}" echo "" read -rsp " Gemini API Key: " API_KEY echo "" if [[ -n "$API_KEY" ]]; then success "API key provided" else warn "No API key provided — set GEMINI_API_KEY env var before using memory search" fi fi fi } # --- Patch clawdbot.json --- patch_config() { step "⚙️ Patching clawdbot.json..." # Check if memorySearch already configured 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})" echo "" 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 back up ${CONFIG_FILE} → ${CONFIG_FILE}.pre-memory-backup" dry "Would add memorySearch config (provider: ${PROVIDER})" if [[ -n "$API_KEY" ]]; then dry "Would add API key to config" fi return fi # Back up config cp "$CONFIG_FILE" "${CONFIG_FILE}.pre-memory-backup" success "Backed up config → ${CONFIG_FILE}.pre-memory-backup" # Build the memorySearch config based on provider local memory_config case "$PROVIDER" in openai) memory_config='{"provider":"openai","model":"text-embedding-3-small"}' if [[ -n "$API_KEY" ]]; then memory_config=$(echo "$memory_config" | jq --arg key "$API_KEY" '. + {remote: {apiKey: $key}}') fi ;; gemini) memory_config='{"provider":"gemini","model":"gemini-embedding-001"}' if [[ -n "$API_KEY" ]]; then memory_config=$(echo "$memory_config" | jq --arg key "$API_KEY" '. + {remote: {apiKey: $key}}') fi ;; local) memory_config='{"provider":"local"}' ;; esac # Merge into config using jq (safe, non-destructive) local tmp_file tmp_file=$(mktemp) jq --argjson ms "$memory_config" ' .agents.defaults.memorySearch = ( (.agents.defaults.memorySearch // {}) * $ms ) ' "$CONFIG_FILE" > "$tmp_file" # Validate the result is valid JSON 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 with memorySearch (${PROVIDER})") 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() { step "📋 Updating AGENTS.md with memory instructions..." # Check if already patched if [[ -f "$AGENTS_FILE" ]] && grep -q "memory_search\|Daily memory log\|Pre-compaction flush" "$AGENTS_FILE" 2>/dev/null; then info "AGENTS.md already contains memory instructions" return fi if $DRY_RUN; then dry "Would append memory instructions to ${AGENTS_FILE}" return fi # Get the patch content local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local patch_file="${script_dir}/config/agents-memory-patch.md" local patch_content if [[ -f "$patch_file" ]]; then patch_content=$(cat "$patch_file") else # Download if running from curl patch_content=$(curl -sL "https://raw.githubusercontent.com/BusyBee3333/clawdbot-memory-system/main/config/agents-memory-patch.md" 2>/dev/null || true) fi if [[ -z "$patch_content" ]]; then # Fallback: inline the patch patch_content=' ## Memory System (auto-added by clawdbot-memory-system installer) ### Mandatory Memory Recall Before answering ANY question about prior work, decisions, or context from previous sessions: 1. Use `memory_search` to find relevant memories 2. Check today'\''s daily log: `memory/YYYY-MM-DD.md` 3. Check yesterday'\''s log if today'\''s is sparse 4. 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, preferences, project progress, action items, blockers - Be specific — future-you needs exact details, not vague summaries ### 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 - Include: what we were working on, where we left off, any pending decisions - This is your safety net against amnesia ### Research Intel System For ongoing research/monitoring projects: - Store in: `memory/{project}-research-intel.md` - Current week'\''s detailed intel at TOP - Compressed 1-3 sentence summaries of previous weeks at BOTTOM - Check for active research intel files on strategic questions ### Git Backup End of session: `cd ~/.clawdbot/workspace && git add -A && git commit -m "session backup" && git push` ' fi if [[ -f "$AGENTS_FILE" ]]; then echo "" >> "$AGENTS_FILE" echo "$patch_content" >> "$AGENTS_FILE" else echo "$patch_content" > "$AGENTS_FILE" fi success "Added memory instructions to AGENTS.md" CHANGES_MADE+=("Updated AGENTS.md with memory habits") } # --- Build Index --- build_index() { step "🔨 Building memory search index..." if $DRY_RUN; then dry "Would run: clawdbot memory index --verbose" dry "Would run: clawdbot memory status --deep" 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 "" info "Indexing memory files..." 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 (this may be normal on first run)" detail "Check: clawdbot memory status --deep" fi echo "" info "Verifying installation..." echo -e "${DIM}" if clawdbot memory status --deep 2>&1; then echo -e "${NC}" success "Memory system verified" else echo -e "${NC}" warn "Status check had warnings (embedding provider may need API key)" fi # Count indexed files FILES_INDEXED=$(find "$MEMORY_DIR" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ') if [[ -f "${WORKSPACE_DIR}/MEMORY.md" ]]; then FILES_INDEXED=$((FILES_INDEXED + 1)) 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 memory/${DIM}YYYY-MM-DD${NC}.md" echo -e " • Search memories before answering about prior work" echo -e " • Flush context before compaction" echo "" echo -e " ${BOLD}3.${NC} Verify anytime with:" 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 it as an env var and remove from config.${NC}" echo "" fi echo -e " ${DIM}Problems? See: https://github.com/BusyBee3333/clawdbot-memory-system#troubleshooting${NC}" echo "" } # --- Print Dry Run Summary --- print_dry_summary() { echo "" echo -e "${BOLD}${CYAN}" echo " ╔══════════════════════════════════════════════╗" echo " ║ 📋 Dry Run Summary ║" echo " ╚══════════════════════════════════════════════╝" echo -e "${NC}" echo "" echo -e " No changes were made. Run without ${CYAN}--dry-run${NC} to apply." 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} if [[ ! "$yn" =~ ^[Yy] ]]; then info "Cancelled" exit 0 fi # Remove memorySearch from config 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 else info "No memorySearch config found" fi # Remove memory instructions from AGENTS.md if [[ -f "$AGENTS_FILE" ]]; then if grep -q "Memory System (auto-added by clawdbot-memory-system installer)" "$AGENTS_FILE" 2>/dev/null; then cp "$AGENTS_FILE" "${AGENTS_FILE}.pre-uninstall-backup" # Remove everything from the memory system header to the end of that section sed -i.bak '/## Memory System (auto-added by clawdbot-memory-system installer)/,$ { /## Memory System (auto-added by clawdbot-memory-system installer)/d; /^## [^M]/!d; }' "$AGENTS_FILE" 2>/dev/null # Simpler approach: use python to remove the section python3 -c " import re with open('${AGENTS_FILE}') as f: content = f.read() # Remove the auto-added section pattern = r'\n*## Memory System \(auto-added by clawdbot-memory-system installer\).*' content = re.sub(pattern, '', content, flags=re.DOTALL) with open('${AGENTS_FILE}', 'w') as f: f.write(content.rstrip() + '\n') " 2>/dev/null || true rm -f "${AGENTS_FILE}.bak" success "Removed memory instructions from AGENTS.md" else info "No auto-added memory instructions found in AGENTS.md" fi fi echo "" success "Uninstall complete" echo "" echo -e " ${DIM}Your memory/ files were preserved.${NC}" echo -e " ${DIM}Config backups saved as .pre-uninstall-backup files.${NC}" echo -e " ${DIM}Run 'clawdbot gateway restart' to apply changes.${NC}" echo "" exit 0 } # ============================================================================ # Main Flow # ============================================================================ # Handle uninstall if $UNINSTALL; then do_uninstall fi # Banner banner if $DRY_RUN; then echo -e " ${CYAN}${BOLD}Running in dry-run mode — no changes will be made${NC}" echo "" fi # Step 1: Detect Clawdbot detect_clawdbot # Step 2: Check dependencies check_deps # Step 3: Check existing files check_existing # Step 4: Choose provider choose_provider # Step 5: Set up memory directory setup_memory_dir # Step 6: Patch clawdbot.json patch_config # Step 7: Patch AGENTS.md patch_agents # Step 8: Build index build_index # Step 9: Summary if $DRY_RUN; then print_dry_summary else print_summary fi ]]>