From e708dd2a8e78ed39de777c7d5f5babb75450cc45 Mon Sep 17 00:00:00 2001 From: Tanmay Karande Date: Sat, 14 Feb 2026 00:02:22 -0500 Subject: [PATCH] added readme and install script --- .env.example | 11 +++ README.md | 249 +++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 244 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 4 files changed, 505 insertions(+), 1 deletion(-) create mode 100755 install.sh diff --git a/.env.example b/.env.example index 3f9fde2..c427051 100644 --- a/.env.example +++ b/.env.example @@ -32,3 +32,14 @@ OPENCODE_TIMEOUT=120 # --- Logging ----------------------------------------------------------------- LOG_LEVEL=INFO + +# --- Claude Code Runtime (alternative to OpenCode) -------------------------- +# Use --claude flag to switch: python main.py --claude +# CLAUDE_MODEL=claude-sonnet-4-20250514 +# CLAUDE_TIMEOUT=120 +# CLAUDE_MAX_TURNS=3 +# CLAUDE_NO_TOOLS=true + +# --- Memory System ----------------------------------------------------------- +# AETHEEL_WORKSPACE=~/.aetheel/workspace +# AETHEEL_MEMORY_DB=~/.aetheel/memory.db diff --git a/README.md b/README.md index e69de29..df50e60 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,249 @@ +

+

⚔️ Aetheel

+

+ A personal AI assistant that lives in Slack — with persistent memory, dual runtimes, and zero cloud dependencies. +

+

+ Quick Start • + Features • + Architecture • + Configuration +

+

+ +--- + +## Quick Start + +### One-line install + +```bash +curl -fsSL http://10.0.0.59:3051/tanmay/Aetheel/raw/branch/main/install.sh | sh +``` + +This will clone the repo, set up a Python virtual environment, install dependencies, and walk you through configuration. + +### Manual install + +```bash +git clone http://10.0.0.59:3051/tanmay/Aetheel.git +cd Aetheel +uv sync # or: pip install -r requirements.txt +cp .env.example .env +# Edit .env with your Slack tokens +``` + +### Run + +```bash +# Default — OpenCode runtime +uv run python main.py + +# Claude Code runtime +uv run python main.py --claude + +# Test mode (echo handler, no AI) +uv run python main.py --test + +# Custom model +uv run python main.py --model anthropic/claude-sonnet-4-20250514 + +# Debug logging +uv run python main.py --log DEBUG +``` + +--- + +## Features + +### 🧠 Persistent Memory System +- **Identity files** — `SOUL.md` (personality), `USER.md` (user profile), `MEMORY.md` (long-term notes) +- **Hybrid search** — 0.7 vector + 0.3 BM25 keyword scoring over all memory files +- **Local embeddings** — `fastembed` (ONNX, BAAI/bge-small-en-v1.5, 384-dim), zero API calls +- **SQLite storage** — FTS5 full-text search + JSON vector embeddings +- **Session logs** — Conversations auto-saved to `daily/YYYY-MM-DD.md` +- **File watching** — Memory re-indexes when you edit `.md` files + +### 🤖 Dual AI Runtimes +| Runtime | Flag | System Prompt | Session Continuity | +|---------|------|---------------|--------------------| +| **OpenCode** (default) | `--cli` / `--sdk` | XML-injected into message | `--continue --session ` | +| **Claude Code** | `--claude` | Native `--system-prompt` flag | `--continue --session-id ` | + +Both return the same `AgentResponse` — zero code changes needed to switch. + +### 💬 Slack Integration +- **Socket Mode** — no public URL or webhook needed +- **DMs + @mentions** — responds in both +- **Thread isolation** — each thread gets its own AI session +- **Chunked replies** — auto-splits long responses at Slack's 4000-char limit + +### ⏰ Action Tags +The AI can perform actions by including tags in its response: + +``` +[ACTION:remind|2|Time to drink water! 💧] +``` + +The system strips the tag from the visible response and schedules the action. + +### 🔒 Local-First +- Embeddings run locally (no OpenAI/Gemini API for memory) +- Memory stored in `~/.aetheel/` — your data stays on your machine +- Only the AI runtime (OpenCode/Claude) makes external API calls + +--- + +## Architecture + +``` +┌──────────────┐ +│ Slack │ Socket Mode (no public URL) +│ Workspace │ +└──────┬───────┘ + │ Events (DM, @mention) + ▼ +┌──────────────┐ ┌──────────────────┐ +│ Slack │────▶│ main.py │ +│ Adapter │ │ (ai_handler) │ +└──────────────┘ └────────┬─────────┘ + │ + ┌─────────┴─────────┐ + ▼ ▼ + ┌──────────────┐ ┌──────────────┐ + │ Memory │ │ AI Runtime │ + │ Manager │ │ (OpenCode │ + │ │ │ or Claude) │ + │ • SOUL.md │ │ │ + │ • USER.md │ │ subprocess │ + │ • MEMORY.md │ │ execution │ + │ • search │ └──────────────┘ + │ • session │ + │ logs │ + └──────────────┘ +``` + +### Project Structure + +``` +aetheel/ +├── main.py # Entry point — handler, memory init, action tags +├── adapters/ +│ └── slack_adapter.py # Slack Socket Mode adapter +├── agent/ +│ ├── opencode_runtime.py # OpenCode CLI/SDK runtime +│ └── claude_runtime.py # Claude Code CLI runtime +├── memory/ +│ ├── manager.py # MemoryManager — sync, search, identity files +│ ├── embeddings.py # Local embeddings via fastembed +│ ├── hybrid.py # Hybrid search (vector + BM25) +│ ├── schema.py # SQLite schema (files, chunks, FTS5) +│ ├── internal.py # Hashing, chunking, file discovery +│ └── types.py # Config, result types +├── docs/ +│ ├── memory-system.md # Memory architecture docs +│ ├── opencode-setup.md # OpenCode install & config guide +│ ├── slack-setup.md # Slack app setup guide +│ └── opencode-integration-summary.md +├── .env.example # Template for secrets +├── pyproject.toml # Dependencies (uv/pip) +└── install.sh # One-click installer +``` + +--- + +## Configuration + +### Required: Slack Tokens + +| Variable | Where to get it | +|----------|----------------| +| `SLACK_BOT_TOKEN` | [api.slack.com/apps](https://api.slack.com/apps) → OAuth & Permissions → Bot Token (`xoxb-...`) | +| `SLACK_APP_TOKEN` | [api.slack.com/apps](https://api.slack.com/apps) → Basic Info → App-Level Tokens (`xapp-...`) | + +See [`docs/slack-setup.md`](docs/slack-setup.md) for full Slack app creation instructions. + +### AI Runtime + +#### OpenCode (default) + +| Variable | Default | Description | +|----------|---------|-------------| +| `OPENCODE_MODE` | `cli` | `cli` (subprocess) or `sdk` (server API) | +| `OPENCODE_MODEL` | auto | Model, e.g. `anthropic/claude-sonnet-4-20250514` | +| `OPENCODE_TIMEOUT` | `120` | CLI timeout in seconds | +| `OPENCODE_SERVER_URL` | `http://localhost:4096` | SDK mode server URL | +| `OPENCODE_WORKSPACE` | `.` | Working directory for OpenCode | + +#### Claude Code (`--claude`) + +| Variable | Default | Description | +|----------|---------|-------------| +| `CLAUDE_MODEL` | auto | Model, e.g. `claude-sonnet-4-20250514` | +| `CLAUDE_TIMEOUT` | `120` | CLI timeout in seconds | +| `CLAUDE_MAX_TURNS` | `3` | Max agentic tool-use turns | +| `CLAUDE_NO_TOOLS` | `true` | Disable tools for pure chat | + +### Memory System + +| Variable | Default | Description | +|----------|---------|-------------| +| `AETHEEL_WORKSPACE` | `~/.aetheel/workspace` | Path to memory files (SOUL.md, etc.) | +| `AETHEEL_MEMORY_DB` | `~/.aetheel/memory.db` | SQLite database path | + +### General + +| Variable | Default | Description | +|----------|---------|-------------| +| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` | + +--- + +## Memory Files + +Aetheel auto-creates three identity files in `~/.aetheel/workspace/`: + +| File | Purpose | +|------|---------| +| `SOUL.md` | Personality, values, communication style | +| `USER.md` | User preferences, context, background | +| `MEMORY.md` | Long-term notes, facts, things to remember | + +Edit these files directly — changes are picked up automatically via file watching. + +Session logs are saved to `~/.aetheel/workspace/daily/YYYY-MM-DD.md` and indexed for search. + +--- + +## Prerequisites + +- **Python 3.14+** (managed via [uv](https://docs.astral.sh/uv/)) +- **Slack workspace** with bot + app tokens +- **One of:** + - [OpenCode](https://opencode.ai) CLI — `curl -fsSL https://opencode.ai/install | bash` + - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — `npm install -g @anthropic-ai/claude-code` + +--- + +## Development + +```bash +# Run tests +uv run python test_memory.py # Memory system smoke test +uv run python test_slack.py # Slack adapter unit tests + +# Check help +uv run python main.py --help +``` + +--- + +## Inspired By + +Built with inspiration from [OpenClaw](https://github.com/nichochar/openclaw) — a TypeScript AI agent framework. Aetheel reimplements the core concepts (memory, hybrid search, session management) in Python with a local-first philosophy. + +--- + +

+ Made with ❤️ and a lot of Claude +

diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..813ba60 --- /dev/null +++ b/install.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env sh +# ============================================================================= +# Aetheel — One-Click Installer +# ============================================================================= +# Usage: +# curl -fsSL http://10.0.0.59:3051/tanmay/Aetheel/raw/branch/main/install.sh | sh +# +# What this script does: +# 1. Checks prerequisites (git, python/uv) +# 2. Clones the repo +# 3. Sets up Python environment & installs dependencies +# 4. Creates .env from template +# 5. Walks you through token configuration +# 6. Optionally starts the bot +# ============================================================================= + +set -e + +# --------------------------------------------------------------------------- +# Colors & Helpers +# --------------------------------------------------------------------------- + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +info() { printf "${BLUE}ℹ${NC} %s\n" "$1"; } +success() { printf "${GREEN}✓${NC} %s\n" "$1"; } +warn() { printf "${YELLOW}⚠${NC} %s\n" "$1"; } +error() { printf "${RED}✗${NC} %s\n" "$1"; } +step() { printf "\n${BOLD}${CYAN}▸ %s${NC}\n" "$1"; } + +# --------------------------------------------------------------------------- +# Banner +# --------------------------------------------------------------------------- + +printf "\n" +printf "${BOLD}${CYAN}" +printf " ╔══════════════════════════════════════════╗\n" +printf " ║ ║\n" +printf " ║ ⚔️ Aetheel Installer ║\n" +printf " ║ Personal AI for Slack ║\n" +printf " ║ ║\n" +printf " ╚══════════════════════════════════════════╝\n" +printf "${NC}\n" + +# --------------------------------------------------------------------------- +# 1. Check Prerequisites +# --------------------------------------------------------------------------- + +step "Checking prerequisites" + +# Git +if command -v git >/dev/null 2>&1; then + success "git $(git --version | awk '{print $3}')" +else + error "git is not installed" + printf " Install: https://git-scm.com/downloads\n" + exit 1 +fi + +# Python / uv +HAS_UV=false +HAS_PYTHON=false + +if command -v uv >/dev/null 2>&1; then + success "uv $(uv --version 2>/dev/null | head -1)" + HAS_UV=true +elif command -v python3 >/dev/null 2>&1; then + PY_VER=$(python3 --version 2>&1 | awk '{print $2}') + success "python3 ${PY_VER}" + HAS_PYTHON=true +elif command -v python >/dev/null 2>&1; then + PY_VER=$(python --version 2>&1 | awk '{print $2}') + success "python ${PY_VER}" + HAS_PYTHON=true +else + error "Neither uv nor python3 found" + printf " Install uv (recommended): ${BOLD}curl -LsSf https://astral.sh/uv/install.sh | sh${NC}\n" + printf " Or install Python 3.14+: https://python.org/downloads\n" + exit 1 +fi + +# AI Runtime (optional check) +if command -v opencode >/dev/null 2>&1; then + success "opencode CLI found" +elif command -v claude >/dev/null 2>&1; then + success "claude CLI found" +else + warn "No AI runtime found (opencode or claude)" + printf " You'll need one of:\n" + printf " • OpenCode: curl -fsSL https://opencode.ai/install | bash\n" + printf " • Claude Code: npm install -g @anthropic-ai/claude-code\n" +fi + +# --------------------------------------------------------------------------- +# 2. Choose Install Directory +# --------------------------------------------------------------------------- + +step "Setting up Aetheel" + +INSTALL_DIR="${AETHEEL_DIR:-$HOME/aetheel}" + +if [ -d "$INSTALL_DIR" ]; then + warn "Directory already exists: $INSTALL_DIR" + printf " Pulling latest changes...\n" + cd "$INSTALL_DIR" + git pull --ff-only 2>/dev/null || warn "Could not pull (you may have local changes)" +else + info "Cloning into $INSTALL_DIR" + git clone http://10.0.0.59:3051/tanmay/Aetheel.git "$INSTALL_DIR" + cd "$INSTALL_DIR" + success "Repository cloned" +fi + +# --------------------------------------------------------------------------- +# 3. Install Dependencies +# --------------------------------------------------------------------------- + +step "Installing dependencies" + +if [ "$HAS_UV" = true ]; then + info "Using uv for dependency management" + uv sync 2>&1 | tail -5 + success "Dependencies installed via uv" +else + info "Using pip for dependency management" + python3 -m venv .venv 2>/dev/null || python -m venv .venv + . .venv/bin/activate + pip install -r requirements.txt 2>&1 | tail -3 + success "Dependencies installed via pip" +fi + +# --------------------------------------------------------------------------- +# 4. Configure .env +# --------------------------------------------------------------------------- + +step "Configuration" + +if [ -f .env ]; then + warn ".env already exists — skipping token setup" + info "Edit $INSTALL_DIR/.env to update your tokens" +else + cp .env.example .env + success "Created .env from template" + + printf "\n" + printf " ${BOLD}You need two Slack tokens to proceed:${NC}\n" + printf " 1. Bot Token (xoxb-...) — OAuth & Permissions page\n" + printf " 2. App Token (xapp-...) — Basic Information → App-Level Tokens\n" + printf "\n" + printf " See: ${CYAN}docs/slack-setup.md${NC} for full instructions\n" + printf "\n" + + # Interactive token entry + printf " ${BOLD}Enter your Slack Bot Token${NC} (xoxb-...) or press Enter to skip: " + read -r BOT_TOKEN + if [ -n "$BOT_TOKEN" ]; then + if command -v sed >/dev/null 2>&1; then + sed -i.bak "s|SLACK_BOT_TOKEN=.*|SLACK_BOT_TOKEN=${BOT_TOKEN}|" .env + rm -f .env.bak + success "Bot token saved" + fi + else + warn "Skipped — edit .env manually before starting" + fi + + printf " ${BOLD}Enter your Slack App Token${NC} (xapp-...) or press Enter to skip: " + read -r APP_TOKEN + if [ -n "$APP_TOKEN" ]; then + if command -v sed >/dev/null 2>&1; then + sed -i.bak "s|SLACK_APP_TOKEN=.*|SLACK_APP_TOKEN=${APP_TOKEN}|" .env + rm -f .env.bak + success "App token saved" + fi + else + warn "Skipped — edit .env manually before starting" + fi +fi + +# --------------------------------------------------------------------------- +# 5. Create Data Directories +# --------------------------------------------------------------------------- + +step "Setting up data directories" + +mkdir -p "$HOME/.aetheel/workspace/daily" +success "Created ~/.aetheel/workspace/" +info "Identity files (SOUL.md, USER.md, MEMORY.md) will be auto-generated on first run" + +# --------------------------------------------------------------------------- +# 6. Done! +# --------------------------------------------------------------------------- + +printf "\n" +printf "${BOLD}${GREEN}" +printf " ╔══════════════════════════════════════════╗\n" +printf " ║ ║\n" +printf " ║ ✅ Aetheel is ready! ║\n" +printf " ║ ║\n" +printf " ╚══════════════════════════════════════════╝\n" +printf "${NC}\n" + +printf " ${BOLD}To start Aetheel:${NC}\n" +printf "\n" +printf " cd %s\n" "$INSTALL_DIR" + +if [ "$HAS_UV" = true ]; then + printf " uv run python main.py\n" +else + printf " source .venv/bin/activate\n" + printf " python main.py\n" +fi + +printf "\n" +printf " ${BOLD}Other commands:${NC}\n" +printf " --claude Use Claude Code instead of OpenCode\n" +printf " --test Echo mode (no AI, for testing Slack)\n" +printf " --help Show all options\n" +printf "\n" +printf " ${BOLD}Files:${NC}\n" +printf " Config: %s/.env\n" "$INSTALL_DIR" +printf " Memory: ~/.aetheel/workspace/\n" +printf " Docs: %s/docs/\n" "$INSTALL_DIR" +printf "\n" + +# Offer to start +printf " ${BOLD}Start Aetheel now?${NC} [y/N] " +read -r START_NOW +if [ "$START_NOW" = "y" ] || [ "$START_NOW" = "Y" ]; then + printf "\n" + info "Starting Aetheel..." + if [ "$HAS_UV" = true ]; then + exec uv run python main.py + else + exec python main.py + fi +fi + +printf "\n" diff --git a/pyproject.toml b/pyproject.toml index 6fd62a0..61486e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "aetheel" version = "0.1.0" -description = "Add your description here" +description = "A personal AI assistant that lives in Slack — with persistent memory, dual runtimes, and zero cloud dependencies." readme = "README.md" requires-python = ">=3.14" dependencies = [