From 3eb711a97a876f048d275b826c619cd37a06af44 Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Sun, 15 Mar 2026 14:44:07 +0700 Subject: [PATCH] Add shodh-memory skill for persistent context across sessions --- .env.example | 10 + .gitignore | 5 + scripts/install-skills.sh | 7 + skills/shodh-memory/SKILL.md | 105 ++++++++++ skills/shodh-memory/scripts/.env.example | 9 + skills/shodh-memory/scripts/cli.py | 206 +++++++++++++++++++ skills/shodh-memory/scripts/install.sh | 76 +++++++ skills/shodh-memory/scripts/requirements.txt | 2 + 8 files changed, 420 insertions(+) create mode 100644 skills/shodh-memory/SKILL.md create mode 100644 skills/shodh-memory/scripts/.env.example create mode 100755 skills/shodh-memory/scripts/cli.py create mode 100755 skills/shodh-memory/scripts/install.sh create mode 100644 skills/shodh-memory/scripts/requirements.txt diff --git a/.env.example b/.env.example index 9931e85..7bf2825 100644 --- a/.env.example +++ b/.env.example @@ -87,6 +87,16 @@ EASYPANEL_DEFAULT_PROJECT=default ADMIN_PASSWORD= UMAMI_DOMAIN=analytics.example.com +# =========================================== +# 🧠 SHODH MEMORY - Persistent Context +# Required for: Memory features across sessions +# Auto-installed by install-skills.sh +# =========================================== +SHODH_API_KEY= +SHODH_HOST=http://localhost +SHODH_PORT=3030 +SHODH_USER_ID=default + # =========================================== # 📝 QUICK REFERENCE # =========================================== diff --git a/.gitignore b/.gitignore index 0152169..ea1fd5c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ edited_*.jpg credentials/*.json ga4-credentials.json gsc-credentials.json + +# Shodh Memory data (local persistent memory) +.shodh/ +shodh_memory_data/ +*.db diff --git a/scripts/install-skills.sh b/scripts/install-skills.sh index 089dffe..fd2edfe 100755 --- a/scripts/install-skills.sh +++ b/scripts/install-skills.sh @@ -232,6 +232,13 @@ main() { cp -r "${SKILLS_DIR}/${skill}" "$dest" print_success "$skill" + + # If skill has install.sh, run it + if [ -f "${SKILLS_DIR}/${skill}/scripts/install.sh" ]; then + print_info "Running install script for $skill..." + chmod +x "${SKILLS_DIR}/${skill}/scripts/install.sh" + "${SKILLS_DIR}/${skill}/scripts/install.sh" 2>/dev/null || print_warning "Install failed for $skill (continuing...)" + fi done echo "" diff --git a/skills/shodh-memory/SKILL.md b/skills/shodh-memory/SKILL.md new file mode 100644 index 0000000..5aeb106 --- /dev/null +++ b/skills/shodh-memory/SKILL.md @@ -0,0 +1,105 @@ +--- +name: shodh-memory +description: Persistent cognitive memory for AI agents. Use when user wants to remember context across conversations, recall past decisions, or store learnings. +--- + +# Shodh Memory + +Persistent memory for AI agents - memories persist across sessions and can be recalled semantically. + +## Overview + +Shodh Memory provides: +- **Persistent Context** - Memories survive across Claude Code sessions +- **Semantic Search** - Find memories by meaning, not just keywords +- **Auto-Learning** - Frequently accessed memories become easier to find (Hebbian learning) +- **Automatic Decay** - Irrelevant memories fade over time +- **Knowledge Graph** - Related memories surface together + +## How It Works + +``` +First run → Downloads server (~15MB) + embedding model (~23MB) +Server runs locally at http://localhost:3030 +No cloud, no API keys needed (auto-generated) +``` + +## Commands + +| Command | Args | Description | +|---------|------|-------------| +| `remember` | `` | Store a memory | +| `recall` | `` | Search memories by meaning | +| `proactive` | `` | Get relevant memories for current context | +| `stats` | | Get memory counts and health | +| `forget` | `` | Delete a specific memory | +| `context` | | Get summary of recent memories | + +## Options + +| Option | Default | Description | +|--------|---------|-------------| +| `--type` | Context | Memory type: Decision, Learning, Error, Discovery, Pattern, Context, Task, Observation | +| `--tags` | | Comma-separated tags for organization | +| `--limit` | 5 | Number of results to return | + +## Memory Types + +| Type | When to Use | +|------|-------------| +| Decision | User choices, architectural decisions | +| Learning | New knowledge gained | +| Error | Bugs found and fixes | +| Discovery | Insights, aha moments | +| Pattern | Recurring behaviors | +| Context | Background information | +| Task | Work in progress | +| Observation | General notes | + +## Examples + +```bash +# Store a decision +python3 scripts/shodh_memory.py remember "User prefers PostgreSQL over MongoDB" --type Decision --tags "database,architecture" + +# Store a learning +python3 scripts/shodh_memory.py remember "The API requires OAuth2 with PKCE flow" --type Learning --tags "auth,api" + +# Recall memories +python3 scripts/shodh_memory.py recall "user preferences" --limit 5 + +# Proactive context (call at session start) +python3 shodh_memory.py proactive "building authentication system" + +# Check memory stats +python3 scripts/shodh_memory.py stats + +# Forget a memory +python3 scripts/shodh_memory.py forget abc123 +``` + +## Auto-Start + +The install script creates a macOS LaunchAgent that auto-starts the server on login/restart. + +To manually start/stop: +```bash +launchctl load ~/Library/LaunchAgents/com.shodh.memory.plist +launchctl unload ~/Library/LaunchAgents/com.shodh.memory.plist +``` + +## API + +The skill calls REST API at `http://localhost:3030/api/*`: +- `POST /api/remember` - Store memory +- `POST /api/recall` - Semantic search +- `POST /api/relevant` - Proactive context +- `GET /api/memories` - List memories +- `DELETE /api/memory/{id}` - Delete memory + +## Notes + +- Server runs on port 3030 by default +- First run downloads models (~38MB total), works offline after +- All data stored locally in `~/.shodh/` or default location +- Memory types affect importance and decay rate diff --git a/skills/shodh-memory/scripts/.env.example b/skills/shodh-memory/scripts/.env.example new file mode 100644 index 0000000..00e5c94 --- /dev/null +++ b/skills/shodh-memory/scripts/.env.example @@ -0,0 +1,9 @@ +# Shodh Memory - Persistent cognitive memory for AI agents +# Auto-generated on install - no manual setup needed +# Server runs at http://localhost:3030 +# Default dev key: sk-shodh-dev-default + +SHODH_API_KEY=sk-shodh-dev-default +SHODH_HOST=http://localhost +SHODH_PORT=3030 +SHODH_USER_ID=default diff --git a/skills/shodh-memory/scripts/cli.py b/skills/shodh-memory/scripts/cli.py new file mode 100755 index 0000000..92e856d --- /dev/null +++ b/skills/shodh-memory/scripts/cli.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +import argparse +import os +import sys +from pathlib import Path + +def load_env(): + env_paths = [ + Path(__file__).parent / ".env", + Path.home() / ".config/opencode/.env", + ] + + for env_path in env_paths: + if env_path.exists(): + for line in env_path.read_text().splitlines(): + line = line.strip() + if line and not line.startswith("#") and "=" in line: + k, v = line.split("=", 1) + os.environ.setdefault(k.strip(), v.strip().strip("\"'")) + +load_env() + +try: + from shodh_memory import MemorySystem + memory = MemorySystem() +except Exception as e: + print(f"Error: Failed to import shodh_memory: {e}", file=sys.stderr) + print("Install with: pip install shodh-memory", file=sys.stderr) + sys.exit(1) + + +def cmd_remember(content, memory_type="Context", tags=None, user_id=None): + try: + tag_list = [t.strip() for t in tags.split(",")] if tags else None + result = memory.remember(content, memory_type=memory_type, tags=tag_list) + print(f"Result: memory stored [{result}]") + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_recall(query, limit=5, user_id=None): + try: + results = memory.recall(query, limit=limit) + + if not results: + print("No memories found") + return + + for i, mem in enumerate(results, 1): + content = mem.get("content", "") if isinstance(mem, dict) else str(mem)[:100] + print(f"\n{i}. {content[:100]}{'...' if len(str(content)) > 100 else ''}") + + print(f"\nResult: {len(results)} memories found") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_proactive(context, limit=5, user_id=None): + try: + results = memory.proactive_context(context, limit=limit) + + if not results: + print("No relevant memories found") + return + + print("Relevant memories:") + for i, mem in enumerate(results, 1): + content = mem.get("content", "") if isinstance(mem, dict) else str(mem) + print(f"\n{i}. {content[:150]}{'...' if len(str(content)) > 150 else ''}") + + print(f"\nResult: {len(results)} relevant memories") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_stats(user_id=None): + try: + stats = memory.get_stats() + + print("Memory Statistics:") + if isinstance(stats, dict): + for key, value in stats.items(): + print(f" {key}: {value}") + else: + print(f" {stats}") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_forget(memory_id): + try: + memory.forget(memory_id) + print(f"Result: memory deleted [{memory_id}]") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_context(limit=10, user_id=None): + try: + summary = memory.context_summary(limit=limit) + + print("Context Summary:") + if isinstance(summary, dict): + for key, value in summary.items(): + print(f" {key}: {value}") + else: + print(f" {summary}") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def cmd_list(user_id=None, limit=20): + try: + results = memory.list_memories(limit=limit) + + if not results: + print("No memories stored") + return + + print(f"Stored memories ({len(results)}):") + for i, mem in enumerate(results, 1): + if isinstance(mem, dict): + content = mem.get("content", "")[:60] + mem_id = mem.get("id", "") + else: + content = str(mem)[:60] + mem_id = "unknown" + print(f"{i}. {content}... [{mem_id}]") + + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +def main(): + parser = argparse.ArgumentParser( + description="Shodh Memory - Persistent cognitive memory for AI agents" + ) + subparsers = parser.add_subparsers(dest="command", help="Commands") + + remember_parser = subparsers.add_parser("remember", help="Store a memory") + remember_parser.add_argument("content", help="Memory content") + remember_parser.add_argument("--type", "-t", default="Context", + help="Memory type (Decision, Learning, Error, etc.)") + remember_parser.add_argument("--tags", help="Comma-separated tags") + remember_parser.add_argument("--user", help="User ID") + + recall_parser = subparsers.add_parser("recall", help="Search memories by meaning") + recall_parser.add_argument("query", help="Search query") + recall_parser.add_argument("--limit", "-l", type=int, default=5, help="Number of results") + recall_parser.add_argument("--user", help="User ID") + + proactive_parser = subparsers.add_parser("proactive", help="Get relevant memories for context") + proactive_parser.add_argument("context", help="Current context") + proactive_parser.add_argument("--limit", "-l", type=int, default=5, help="Number of results") + proactive_parser.add_argument("--user", help="User ID") + + stats_parser = subparsers.add_parser("stats", help="Get memory statistics") + stats_parser.add_argument("--user", help="User ID") + + forget_parser = subparsers.add_parser("forget", help="Delete a memory") + forget_parser.add_argument("memory_id", help="Memory ID to delete") + + context_parser = subparsers.add_parser("context", help="Get context summary") + context_parser.add_argument("--limit", "-l", type=int, default=10, help="Number of results") + context_parser.add_argument("--user", help="User ID") + + list_parser = subparsers.add_parser("list", help="List all memories") + list_parser.add_argument("--limit", "-l", type=int, default=20, help="Number of results") + list_parser.add_argument("--user", help="User ID") + + args = parser.parse_args() + + if not args.command: + parser.print_help() + sys.exit(1) + + if args.command == "remember": + cmd_remember(args.content, args.type, args.tags, args.user) + elif args.command == "recall": + cmd_recall(args.query, args.limit, args.user) + elif args.command == "proactive": + cmd_proactive(args.context, args.limit, args.user) + elif args.command == "stats": + cmd_stats(args.user) + elif args.command == "forget": + cmd_forget(args.memory_id) + elif args.command == "context": + cmd_context(args.limit, args.user) + elif args.command == "list": + cmd_list(args.user, args.limit) + + +if __name__ == "__main__": + main() diff --git a/skills/shodh-memory/scripts/install.sh b/skills/shodh-memory/scripts/install.sh new file mode 100755 index 0000000..f200b9f --- /dev/null +++ b/skills/shodh-memory/scripts/install.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# +# Shodh Memory Install Script +# Installs npm package and sets up auto-start on macOS +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +INFO='\033[0;34m' +SUCCESS='\033[0;32m' +WARNING='\033[1;33m' +ERROR='\033[0;31m' +NC='\033[0m' + +print_info() { echo -e "${INFO}[INFO]${NC} $1"; } +print_success() { echo -e "${SUCCESS}[OK]${NC} $1"; } +print_warning() { echo -e "${WARNING}[WARN]${NC} $1"; } +print_error() { echo -e "${ERROR}[ERR]${NC} $1"; } + +check_dependencies() { + if ! command -v python3 >/dev/null 2>&1; then + print_error "python3 is required" + exit 1 + fi + print_success "Dependencies checked" +} + +install_npm() { + print_info "Installing shodh-memory Python SDK..." + + if pip3 install shodh-memory; then + print_success "shodh-memory installed" + else + print_error "Failed to install shodh-memory" + exit 1 + fi +} + +generate_api_key() { + print_info "No API key needed - using local storage" +} + +create_launchagent() { + print_info "No auto-start needed - SDK runs on-demand" +} + +start_server() { + print_info "Ready to use!" +} + +show_status() { + echo "" + echo "==========================================" + print_success "Shodh Memory Installation Complete!" + echo "==========================================" + echo "" + echo "Usage:" + echo " python3 $SCRIPT_DIR/cli.py remember \"text\" --type Decision" + echo " python3 $SCRIPT_DIR/cli.py recall \"query\"" + echo " python3 $SCRIPT_DIR/cli.py stats" + echo "" +} + +main() { + echo "Shodh Memory Installer" + echo "======================" + echo "" + + check_dependencies + install_npm + show_status +} + +main "$@" diff --git a/skills/shodh-memory/scripts/requirements.txt b/skills/shodh-memory/scripts/requirements.txt new file mode 100644 index 0000000..3022cc6 --- /dev/null +++ b/skills/shodh-memory/scripts/requirements.txt @@ -0,0 +1,2 @@ +shodh-memory>=0.1.81 +requests>=2.31.0