PowerMem

July 1, 2026 · View on GitHub

This file is a prompt for Claude Code. Open Claude Code in your terminal and say:

Read and follow apps/claude-code-plugin/SETUP.md to set up PowerMem memory for Claude Code.

Claude Code will then run the steps below: detect whether you are in the PowerMem source tree or not, ask you for the few required secrets, and wire PowerMem up as a globally enabled plugin so every claude session (interactive AND non-interactive claude -p) uses it automatically — no per-session --plugin-dir flag.


Installed plugin initialization

Use this section when the memory-powermem plugin is already installed from a Claude Code marketplace and the user runs:

/memory-powermem:init

In this mode, do not run the source/developer install flow below: do not build hook binaries, do not stage the plugin, do not run claude plugin marketplace add, do not run claude plugin install, and do not build the dashboard. The plugin is already installed; this section only prepares the PowerMem backend that the plugin connects to.

Installed-plugin init ensures uv is available, then starts the backend with the uvx-style launcher. The package spec depends on the storage backend: the default SQLite path uses powermem[server,extras] (pulls sentence-transformers for the local huggingface embedder), while the OceanBase path uses powermem[server,seekdb]. It does not create a plugin-local venv. Therefore the PyPI release used by this flow must already contain the backend capabilities required by the plugin, including the local embedding dependencies. If the user is validating a plugin change that depends on unpublished backend code, use POWERMEM_INIT_PACKAGE to pass that exact Git branch or commit to uvx --from instead of the PyPI package.

Installed-plugin init is idempotent and uses plugin-local state:

$HOME/.powermem/
  .env
  runtime.env
  powermem.pid
  powermem-server.log
  seekdb_data/

Follow these steps:

Always use a two-step invocation: discover or reuse CLAUDE_PLUGIN_ROOT first, then run the script. Never write VAR=val sh "$VAR/..." on one line — the shell expands $VAR before the assignment, producing an empty path.

# If CLAUDE_PLUGIN_ROOT is not already set, find the plugin root:
if [ -z "${CLAUDE_PLUGIN_ROOT:-}" ]; then
  export CLAUDE_PLUGIN_ROOT=$(find ~/.claude/plugins/cache/powermem/memory-powermem -maxdepth 2 -name scripts -type d 2>/dev/null | head -1 | xargs dirname)
fi
sh "$CLAUDE_PLUGIN_ROOT/scripts/..."
  1. If the skill was just installed or updated, ask the user to run /reload-plugins first, then retry /memory-powermem:init.

  2. Run sh "$CLAUDE_PLUGIN_ROOT/scripts/status.sh" and inspect whether config, uv, managed PID, Python versions, and health are present.

  3. If the server is healthy and .env exists — tell the user the current storage backend (read DATABASE_PROVIDER from .env). Do not re-run init.sh. If the user wants to reconfigure, stop the server first, then proceed.

  4. If .env is missing, run init with auto-detection first:

    sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"
    

    The script reads the current process environment first and attempts to derive the supported Anthropic configuration. It uses ANTHROPIC_API_KEY first; only when that is absent does it use ANTHROPIC_AUTH_TOKEN together with ANTHROPIC_BASE_URL. It also reads ANTHROPIC_MODEL from the environment. If the environment does not provide a complete config, it falls back to ~/.claude/settings.json using the same Anthropic keys. It writes the plugin-local .env with the full PowerMem backend defaults: SQLite storage (default for coding agent; set POWERMEM_INIT_DATABASE_PROVIDER=oceanbase for OceanBase/seekdb production use), local HuggingFace embedding (no API key, sentence-transformers from powermem[extras]), server settings, and logging settings.

  5. If init reports missing values, ask the user only for those missing values. Do not invent credentials. Re-run init with the matching environment variables:

    POWERMEM_INIT_LLM_PROVIDER=anthropic \
    POWERMEM_INIT_LLM_MODEL=anthropic/claude-sonnet-4.6 \
    POWERMEM_INIT_LLM_API_KEY=... \
    sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"
    

    For a bearer-token gateway, use:

    POWERMEM_INIT_LLM_PROVIDER=anthropic \
    POWERMEM_INIT_LLM_MODEL=anthropic/claude-sonnet-4.6 \
    POWERMEM_INIT_LLM_AUTH_TOKEN=... \
    POWERMEM_INIT_LLM_BASE_URL=https://your-gateway.example.com \
    sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"
    

    Optional variables:

    • POWERMEM_INIT_DATABASE_PROVIDER: storage backend — sqlite (default, coding agent) or oceanbase (production/cluster). Invalid values fall back to sqlite.
    • POWERMEM_INIT_LLM_BASE_URL for a custom provider gateway.
    • POWERMEM_INIT_PACKAGE to test unpublished backend code through uvx --from instead of PyPI powermem. Match the extras to the storage backend: powermem[server,extras] for SQLite (default, includes sentence-transformers), powermem[server,seekdb] for OceanBase. Example: powermem[server,extras] @ git+https://github.com/oceanbase/powermem.git@<branch-or-sha>.
    • POWERMEM_INIT_PYTHON to force a specific Python >= 3.11.
    • POWERMEM_INIT_PORT to force the managed server port.
  6. Never print API keys, auth tokens, or other credentials. Mask any secret in summaries.

  7. After init succeeds, run sh "$CLAUDE_PLUGIN_ROOT/scripts/status.sh" again and report the base URL.

  8. The hook launcher reads runtime.env, so once init writes a base URL, prompt recall and session-save hooks use that backend automatically.

The default local embedding model (all-MiniLM-L6-v2) is downloaded automatically by PowerMem at startup — cache hit loads from disk via SentenceTransformer(local_files_only=True); cache miss on CN networks downloads through ModelScope and bridges into the HuggingFace hub cache; cache miss elsewhere downloads from HuggingFace with a 30s timeout. POWERMEM_INIT_PRELOAD_MODEL is deprecated and now a no-op; init prints a deprecation message if it is set.

If startup fails with No module named 'sentence_transformers', the backend package resolved by uvx --from does not include the local embedding dependency. Publish a backend build that includes it, or set POWERMEM_INIT_PACKAGE to a Git branch/commit that does.

To test connectivity:

curl -s -m 10 -o /dev/null -w "ModelScope: HTTP %{http_code}\n" \
  https://www.modelscope.cn/api/v1/models/AI-ModelScope/all-MiniLM-L6-v2
curl -s -m 10 -o /dev/null -w "HuggingFace: HTTP %{http_code}\n" \
  https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2

Set up PowerMem memory for Claude Code on this machine globally. Do the whole integration autonomously and ask me for any secret you need — never invent credentials.

🔒 DATA SAFETY — API Key Masking (MANDATORY): When displaying ANY .env content — current values, proposed changes, confirmation summaries, or any other output — you MUST mask LLM_API_KEY, LLM_AUTH_TOKEN, and any other secret values (passwords, tokens, keys):

  • Key ≥ 10 chars: show only first 4 + last 4 characters (e.g. sk-a…b12x)
  • Key < 10 chars: show *** Non-secret values (provider, model, base URLs, storage type, etc.) may be shown in full.

🔒 Shell commands MUST also avoid printing secrets. Terminal output is visible to the user and Claude Code cannot retroactively redact it. Follow these rules:

  • When reading env vars or .env, use inline masking so the shell never prints plaintext: echo "${VAR:0:4}...${VAR: -4}" to show first 4 + last 4, or [ -n "$VAR" ] && echo "set" || echo "empty" just to check existence.
  • When you need to cat .env or grep for secrets, pipe through sed to mask before printing: cat .env | sed -E 's/(API_KEY=).*/\1***REDACTED***/'
  • Never run echo $LLM_API_KEY, echo $LLM_AUTH_TOKEN, env | grep KEY, cat .env (unmasked), or any command that would print a secret value directly to stdout.
  • Use read with -s (silent) when prompting for secrets interactively.

⚠️ .env changes always require user approval. Before modifying .env for any reason (LLM config, embedder settings, storage switches, ...), show the user the masked current values (using the rules above), propose the exact change, and WAIT for confirmation. Never patch .env silently.

This procedure is idempotent: it is safe to re-run. Each step must detect existing state and either skip, reuse, or refresh it instead of failing or duplicating work.

  1. DETECT CONTEXT. The current directory is the PowerMem source tree if a pyproject.toml here has name = "powermem" (or src/powermem/ and apps/claude-code-plugin/ both exist). Tell me which path you will take:
    • SOURCE -> deploy from this checkout and install the Claude Code plugin GLOBALLY in HTTP mode (hooks -> REST; rebuild hook binaries only when refreshing them from source changes).
    • PYPI/MCP -> install PowerMem from PyPI with uv and connect via the powermem-mcp server (the plugin itself is NOT on PyPI).

⚠️ RULE: Every time you need to modify .env — for any reason, even a single variable — you MUST stop and ask the user what value to use. Show the masked current content of the relevant lines (per the 🔒 DATA SAFETY rules above), propose the change, and WAIT for the user's confirmation before writing. Never silently patch .env.

1a. DETECT REGION (run before any network operations). Detect whether this machine is in China — this determines uv install source, model download source, and Python package index:

CC=$(curl -s -m 5 https://ipinfo.io/country 2>/dev/null || echo "UNKNOWN")
echo "Region: $CC"
  • If CC=CN: install uv through the USTC GitHub Release mirror, use ModelScope for model downloads, and set the Python package index to https://pypi.tuna.tsinghua.edu.cn/simple/.
  • If CC != CN (or UNKNOWN): install uv through the official Astral installer, use HuggingFace for model downloads, and use the default PyPI index.
  • Store CC in a shell variable — every uv install, package install, and model download step below branches on this value. Re-check on every re-run (region may change if the machine moves or VPN state changes).

uv installation (run before any Python package install):

if ! command -v uv >/dev/null 2>&1; then
  if [ "$CC" = "CN" ]; then
    export UV_DOWNLOAD_URL=https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/
    curl -sL https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/uv-installer.sh | sh
  else
    curl -LsSf https://astral.sh/uv/install.sh | sh
  fi
  export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
fi
uv --version

uv pip conventions (apply to ALL package installs in this file):

a. CN mirror — if CC=CN, add --default-index https://pypi.tuna.tsinghua.edu.cn/simple to every uv pip install command. Non-CN (or UNKNOWN) uses the default PyPI index with no extra flags.

b. Retry — on failure, retry up to 3 times. If a uv pip install command fails, do NOT immediately escalate to the user. Retry the EXACT same command up to 2 more times (3 total attempts). If all 3 fail, only then report the error. Between retries, wait 2 seconds: bash for i in 1 2 3; do uv pip install --python "$POWERMEM_PYTHON" ... && break echo "uv pip install attempt $i failed, retrying in 2s..." sleep 2 done This applies to editable installs, dependency installs, model download dependencies, etc.

c. All four package install locations affected by (a)-(b): 1. uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]' 2. uv pip install --python "$POWERMEM_PYTHON" -q modelscope 3. uv pip install --python "$POWERMEM_PYTHON" "powermem[server,seekdb]" 4. uv pip install --python "$POWERMEM_PYTHON" -q huggingface_hub

  1. COLLECT CONFIG (idempotent). If a .env already exists in the working directory with LLM_PROVIDER / LLM_API_KEY or LLM_AUTH_TOKEN / LLM_MODEL set to real values (not placeholders like your_api_key_here), REUSE it — skip directly to step 3a/3b. Only collect what is missing. Use zero-config defaults for everything else (storage = platform-aware: embedded SeekDB on supported Linux installs, otherwise SQLite basic mode; embedder = local all-MiniLM-L6-v2) unless I say otherwise.

    2a. Auto-detect or manual? Use AskUserQuestion (single-select):

    OptionDescription
    Yes, auto-detectAuto-detect LLM config from the current process environment, then ~/.claude/settings.json, then ask only for missing fields

    If the user selects "Yes, auto-detect" (or "Other" and types "yes"/"auto"):

    Auto-detection priority chain:

    1. Current process environment variables — check these first:
      • ANTHROPIC_API_KEY
      • ANTHROPIC_AUTH_TOKEN
      • ANTHROPIC_BASE_URL
      • ANTHROPIC_MODEL
    2. ~/.claude/settings.json — use env.ANTHROPIC_*, env.LLM_*, and top-level model as fallback sources
    3. Manual input — ask only for fields that are still missing

    PowerMem supports Claude Code's Anthropic API-key path and bearer-token gateway path. Do not treat ANTHROPIC_AUTH_TOKEN as LLM_API_KEY; copy it to LLM_AUTH_TOKEN and require ANTHROPIC_BASE_URL. A token without a base URL is incomplete; fall back to API-key mode or ask for the base URL instead of sending the token to Anthropic's default endpoint. Do not migrate CLAUDE_CODE_OAUTH_TOKEN, /login credentials, apiKeyHelper, Bedrock, Vertex, or Foundry as either LLM_API_KEY or LLM_AUTH_TOKEN.

    Step 1 — Check OS environment variables. Run these checks silently (do not print the values, only note whether each field was found):

    FieldCheck
    LLM_PROVIDERIf ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN is set → anthropic; otherwise infer from ANTHROPIC_MODEL prefix if present
    LLM_MODEL$ANTHROPIC_MODEL
    LLM_API_KEY$ANTHROPIC_API_KEY
    LLM_AUTH_TOKEN$ANTHROPIC_AUTH_TOKEN, only when ANTHROPIC_API_KEY is absent
    LLM_BASE_URL$ANTHROPIC_BASE_URL

    If environment model detection fails, read env.ANTHROPIC_MODEL, env.LLM_MODEL, or top-level model from ~/.claude/settings.json. Keep the model exactly as configured. Do not strip a <provider>/ prefix and do not rewrite dotted versions:

    • "anthropic/claude-opus-4.6"LLM_PROVIDER=anthropic, LLM_MODEL=anthropic/claude-opus-4.6
    • "anthropic/claude-sonnet-4.6"LLM_PROVIDER=anthropic, LLM_MODEL=anthropic/claude-sonnet-4.6

    Credentials — preserve PowerMem's environment precedence: API key first, bearer-token gateway second. If environment credentials are incomplete, fall back to the old ~/.claude/settings.json credential flow. Read:

    • ANTHROPIC_API_KEY
    • ANTHROPIC_AUTH_TOKEN, only if ANTHROPIC_API_KEY is absent
    • fallback env.ANTHROPIC_AUTH_TOKEN / env.ANTHROPIC_API_KEY from ~/.claude/settings.json

    Base URL — read directly:

    • ANTHROPIC_BASE_URL
    • fallback env.ANTHROPIC_BASE_URL or env.LLM_BASE_URL from ~/.claude/settings.json
    • If ANTHROPIC_AUTH_TOKEN is used, ANTHROPIC_BASE_URL is required.
    • If ANTHROPIC_API_KEY is used and the base URL is absent, leave it blank — PowerMem will use the provider's default endpoint.

    Step 3 — For any fields still missing after environment and settings detection, ask as a plain chat question (one at a time, per 2b–2e below). Only ask for what is actually missing.

    After detection/manual input, show a masked summary of the merged result (per 🔒 DATA SAFETY rules), noting the source of each field (env / settings.json / manual). Then jump to 2f.

    If the user does NOT select auto-detect, fall back to the manual flow:

    2b. Ask: "Any custom base URL? (paste it, or say no to use the default)"

    2c. Ask: "What provider id? (e.g. openai, anthropic, qwen, deepseek, ollama)"

    2d. Ask for the credential: API key for direct provider access, or auth token for an Anthropic-compatible gateway. Skip if provider is ollama or vllm.

    2e. Ask: "Which model? (e.g. gpt-4o-mini, claude-sonnet-4-6, qwen-plus)"

    For 2b–2e, ask each question as a plain chat message, one at a time. Do NOT use AskUserQuestion for these free-text inputs.

    2f. Confirm and write. Show a masked summary of what will be written (per 🔒 DATA SAFETY rules above), then WAIT for explicit "yes" before writing. Copy .env.example if .env does not exist, then fill LLM_PROVIDER / LLM_API_KEY or LLM_AUTH_TOKEN / LLM_MODEL. If a base URL was given, write it to the provider-prefixed *_LLM_BASE_URL (e.g. OPENAI_LLM_BASE_URL) — verify spelling against .env.example.full; a typo is silently ignored.

3a. SOURCE path (global install): - Ensure uv is installed using Step 1a, then create/reuse an explicit Python environment and install PowerMem with uv: bash uv venv venv --python python3.11 POWERMEM_PYTHON="$(pwd)/venv/bin/python" export PATH="$(pwd)/venv/bin:$PATH" uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]' ⚠️ Both extras are required: [server] adds fastapi/uvicorn and fastmcp for the HTTP API plus MCP transports; [seekdb] adds the embedded seekdb storage backend. Platforms where embedded SeekDB is unavailable default to SQLite basic mode; use OceanBase/SeekDB for the full capability stack. - Immediately after uv pip install, detect which Python interpreter was used. Read the shebang from the freshly-installed powermem-server entry point — this is the only reliable way to guarantee that the model-download script, the uv call inside it, and the server all use the exact same interpreter and site-packages. On many Linux systems the default python points to a version below 3.11 and python3 may point to a different minor version than what uv used; relying on bare python will silently use the wrong environment and the download will fail with an ImportError: POWERMEM_PYTHON=(head1"(head -1 "(command -v powermem-server)"
| sed 's|#!||;s| .*||') echo "Using interpreter: $POWERMEM_PYTHON" # e.g. /usr/bin/python3.11 - Start the embedding model download in the background immediately after the uv package install, so it runs in parallel with the remaining setup steps (hook build, plugin stage/install). Branch on the region detected in Step 1a:

  **CN region (ModelScope path):**
    uv pip install --python "$POWERMEM_PYTHON" -q modelscope && $POWERMEM_PYTHON -c "
    from modelscope import snapshot_download
    snapshot_download('AI-ModelScope/all-MiniLM-L6-v2')
    import os, shutil, urllib.request, json
    src = os.path.expanduser('~/.cache/modelscope/hub/models/AI-ModelScope/all-MiniLM-L6-v2')
    hub = os.path.expanduser('~/.cache/huggingface/hub/models--sentence-transformers--all-MiniLM-L6-v2')
    try:
        resp = urllib.request.urlopen('https://huggingface.co/api/models/sentence-transformers/all-MiniLM-L6-v2', timeout=5)
        rev = json.load(resp)['sha']
    except Exception:
        rev = 'fa97f6e7cb1a59073dff9e9d8ba1c7c1591cc08d'
    snap = os.path.join(hub, 'snapshots', rev)
    os.makedirs(snap, exist_ok=True)
    os.makedirs(os.path.join(hub, 'refs'), exist_ok=True)
    open(os.path.join(hub, 'refs', 'main'), 'w').write(rev)
    skip = {'configuration.json', 'data_config.json'}
    [shutil.copytree(os.path.join(src,n), os.path.join(snap,n))
     if os.path.isdir(os.path.join(src,n))
     else shutil.copy2(os.path.join(src,n), os.path.join(snap,n))
     for n in os.listdir(src)
     if n not in skip and not os.path.exists(os.path.join(snap,n))]
    print('Model download and cache bridge complete.')
    " >> /tmp/powermem-model-download.log 2>&1 &

  **Non-CN region (HuggingFace path):**
    uv pip install --python "$POWERMEM_PYTHON" -q huggingface_hub && $POWERMEM_PYTHON -c "
    from huggingface_hub import snapshot_download
    snapshot_download('sentence-transformers/all-MiniLM-L6-v2')
    print('Model download complete.')
    " >> /tmp/powermem-model-download.log 2>&1 &

  Store the background PID:
    POWERMEM_MODEL_DL_PID=$!
- Ask whether to build the Web Dashboard EARLY — before starting the server —
  so the build runs in parallel with model download and server startup. Use
  AskUserQuestion (single-select):

  | Option | Description |
  |--------|-------------|
  | Yes, build dashboard | Run `make build-dashboard` in background, parallel with model download and server startup |
  | No, skip | Continue without building the dashboard |

  If the user selects "Yes, build dashboard", launch it in the BACKGROUND
  immediately (do NOT wait for it — model download starts in parallel too):
    make build-dashboard >> /tmp/powermem-dashboard-build.log 2>&1 &
    DASHBOARD_BUILD_PID=$!
- Confirm the hook binaries are present before install, because they get copied
  into Claude's plugin cache at install time:
    normal Git/marketplace install: use the committed hooks/bin/ binaries.
    if hook source changed and Go 1.22+ is present:  make build-claude-hook
    if refreshed binaries are needed but Go is absent: offer to install Go or
    fall back to the PYPI/MCP path below.
- Ensure the plugin's root .mcp.json stays empty ({}) — default HTTP mode.
- STAGE the plugin into a stable, Claude-owned location so the marketplace does
  NOT depend on this checkout — you can move or delete the repo afterwards and
  memory keeps working. Copy the whole plugin dir (built binaries included) into
  ~/.claude/marketplaces/powermem:
    DEST="$HOME/.claude/marketplaces/powermem"
    mkdir -p "$DEST"
    rsync -a --delete "<ABS_PATH>/apps/claude-code-plugin/" "$DEST/"
      # no rsync? rm -rf "$DEST" && cp -a "<ABS_PATH>/apps/claude-code-plugin/." "$DEST/"
  The committed `hooks/bin/` binaries are already on disk before this copy.
  Re-copy on every re-run so the staged dir tracks your latest build.
- Register the marketplace from the STAGED dir (it ships
  .claude-plugin/marketplace.json) — never from the repo:
    claude plugin marketplace add "$DEST"
  If it reports "already on disk", refresh it instead:
    claude plugin marketplace update powermem
- Install + enable the plugin globally (user scope). Install auto-enables it:
    claude plugin install memory-powermem@powermem --scope user
  IMPORTANT idempotency rule: a plain re-install is a no-op and does NOT refresh
  the cached copy. If you just rebuilt the binaries or changed the plugin, force
  a refresh regardless of whether the plugin was previously installed:
    claude plugin uninstall memory-powermem@powermem 2>/dev/null || true
    claude plugin install   memory-powermem@powermem --scope user
  The `|| true` swallows the "not found" error when the plugin was never installed
  — uninstall fails with exit code 1 in that case, which would abort a script.
  (Enablement is preserved across uninstall+reinstall.)
- Wait for the background model download (started above) to finish before
  starting the server. If it is already done this is a no-op:
    if [ -n "${POWERMEM_MODEL_DL_PID:-}" ] && kill -0 "$POWERMEM_MODEL_DL_PID" 2>/dev/null; then
      echo "Waiting for model download to finish..."
      wait "$POWERMEM_MODEL_DL_PID"
    fi
    grep -q "complete" /tmp/powermem-model-download.log 2>/dev/null \
      || { echo "Model download failed. Check /tmp/powermem-model-download.log"; exit 1; }
- Start the API server only if it is not already healthy (idempotent).
  ⚠️ STARTUP TIME: first launch can take 60–120s (local embedder load/download + uvicorn bind).
  Exit code 7 means the port is not yet bound — do NOT kill and restart.
    curl -s http://localhost:8848/api/v1/system/health | grep -q healthy \
      || { powermem-server --host 0.0.0.0 --port 8848 &
           echo "Waiting for server (first launch can take 60–120s)..."
           for i in $(seq 1 30); do
             sleep 5
             curl -s -m 3 http://localhost:8848/api/v1/system/health \
               | grep -q healthy \
               && echo "Server ready after $((i*5))s." && break
             echo "  still starting... ($((i*5))s)"
           done; }
- **After the server is healthy**, auto-detect dashboard availability:
  ```bash
  # Wait for dashboard build to finish (if started):
  if [ -n "${DASHBOARD_BUILD_PID:-}" ] && kill -0 "$DASHBOARD_BUILD_PID" 2>/dev/null; then
    echo "Waiting for dashboard build to finish..."
    wait "$DASHBOARD_BUILD_PID"
  fi
  # Auto-detect whether dashboard is available:
  if curl -s -m 3 http://localhost:8848/dashboard/ | grep -q '<title>PowerMem'; then
    echo "Dashboard is available at http://localhost:8848/dashboard/"
    # open in browser if possible
    command -v xdg-open >/dev/null 2>&1 && xdg-open http://localhost:8848/dashboard/ &
    command -v open >/dev/null 2>&1 && open http://localhost:8848/dashboard/ &
  else
    echo "Dashboard build may have failed. Check /tmp/powermem-dashboard-build.log"
  fi
  ```
  The running server is not restarted. Dashboard assets are served from the
  static files directory configured in the server.

- Confirm the plugin is enabled:  claude plugin list  (look for
  memory-powermem@powermem). Do NOT print a --plugin-dir command — it is global
  now; every `claude` and `claude -p` loads it automatically.

3b. PYPI/MCP path: - Install the server extra in the environment that Claude will use, then: uv venv venv --python python3.11 POWERMEM_PYTHON="(pwd)/venv/bin/python"exportPATH="(pwd)/venv/bin/python" export PATH="(pwd)/venv/bin:PATH"uvpipinstallpython"PATH" uv pip install --python "POWERMEM_PYTHON" "powermem[server,seekdb]" powermem-mcp --help - Register the MCP server globally so it persists across sessions (stdio = no port), run from the directory holding the .env. Idempotent: if claude mcp get powermem already exists, remove it first, then add: claude mcp remove powermem 2>/dev/null; claude mcp add powermem -- powermem-mcp stdio - If registration succeeds but Claude reports the MCP server as failed, debug the MCP process before retrying setup: claude mcp list claude mcp get powermem command -v powermem-mcp powermem-mcp --help Then run the MCP server directly from the same directory that contains .env and capture stderr separately: powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr & MCP_PID=!;sleep10;kill"!; sleep 10; kill "MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true sed -n '1,120p' /tmp/powermem-mcp.stderr sed -n '1,40p' /tmp/powermem-mcp.stdout Expected: stderr may contain normal startup diagnostics; stdout must be empty until a JSON-RPC request arrives. If stdout contains banners, warnings, stack traces, or any non-JSON text, Claude cannot parse the MCP stream. Fix that output pollution before claiming the MCP path works.

  1. VERIFY with a real round-trip — do not claim success without data. Run the exact commands below and substitute nothing except the noted placeholder. Do NOT mark this step done until you have seen a non-empty search result actually come back.

    SOURCE/HTTP path — run a/b/c/d/e in order:

    a. Confirm the server answers (output must contain "status":"healthy"): curl -s -m 5 http://localhost:8848/api/v1/system/health

    b. WRITE a probe memory. CRITICAL SCHEMA: the request body is a single "content" STRING field — NOT a mem0-style "messages" array. Sending {"messages":[...]} returns HTTP 422 {"detail":[{"type":"missing","loc":["body","content"]...}]}. Use a unique user_id so the probe is isolated from real data: curl -s -m 60 -X POST http://localhost:8848/api/v1/memories
    -H 'Content-Type: application/json'
    -d '{"content":"PowerMem setup probe: my favorite test fruit is dragonfruit-zx9.","user_id":"powermem_setup_probe"}' Expected: JSON with "success": true and a data[0].memory_id (a long numeric string). The call can take 10-30s because the LLM extracts facts — KEEP the -m 60 timeout and WAIT; do not background it or abort early. Save the returned data[0].memory_id (a.k.a. data[0].id) — you need it for cleanup in (e).

    c. SEARCH it back. CRITICAL SCHEMA: the body field is "query" (not "question" or "text"), with the SAME user_id you wrote with: curl -s -m 30 -X POST http://localhost:8848/api/v1/memories/search
    -H 'Content-Type: application/json'
    -d '{"query":"what is my favorite test fruit","user_id":"powermem_setup_probe","limit":5}' Expected: data.total >= 1 and data.results[0].content mentions dragonfruit-zx9. If data.total is 0 the round-trip FAILED — do NOT report success. Re-check the server log and the embedder, retry the write in (b) once, then escalate to me.

    d. SHOW me both the write JSON and the search JSON (this is the proof of success).

    e. CLEAN UP the probe — delete by the id from (b), then confirm it is gone: curl -s -m 10 -X DELETE http://localhost:8848/api/v1/memories/<MEMORY_ID> Re-run the search from (c): data.total must now be 0.

    f. BONUS (proves global + headless wiring; do it if you can). Run a headless prompt from an UNRELATED dir with NO --plugin-dir, then check the logs for the two hook-driven calls it triggers: ( cd /tmp && claude -p "Reply with exactly: probe ok" ) Then in server.log (powermem-server), AFTER that run, you MUST see both: POST /api/v1/memories/search <- UserPromptSubmit hook (auto-recall) POST /api/v1/memories <- SessionEnd hook (auto-save) Seeing both proves PowerMem loads automatically in every claude/claude -p.

    PYPI/MCP path: confirm claude mcp list shows powermem as "connected" (not "failed"). If it shows failed, run the MCP diagnostics below and do not report success until the direct MCP process starts cleanly and Claude shows it connected.

    a. Inspect Claude's registered MCP config and status: claude mcp list claude mcp get powermem Verify the command is exactly powermem-mcp stdio unless you intentionally chose a different binary. Also verify command -v powermem-mcp succeeds in the same shell where claude runs.

    b. Run the MCP server directly and inspect both streams: powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr & MCP_PID=!;sleep10;kill"!; sleep 10; kill "MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true sed -n '1,120p' /tmp/powermem-mcp.stderr sed -n '1,40p' /tmp/powermem-mcp.stdout Interpret the output: - stderr has import errors: install/repair the package or virtual environment. - stderr has missing LLM/API key/auth token/config errors: fix .env after asking the user for approval; never silently patch .env. - stderr has model download or network timeouts: pre-download the model or switch to a configured remote embedder. - stdout has non-JSON text before any request: remove noisy prints/logging from stdout; MCP stdio stdout is protocol-only. - both files are empty after timeout: the process likely started and waited for JSON-RPC input; continue with Claude-side connection checks.

    c. If direct startup is clean but Claude still says failed, refresh the registration: claude mcp remove powermem claude mcp add powermem -- powermem-mcp stdio claude mcp list If it still fails, start Claude with debug logging if available in the installed Claude Code version, then look for the first MCP spawn or JSON-RPC parse error.

  2. SUMMARIZE: path taken, where .env lives, where the staged marketplace lives (~/.claude/marketplaces/powermem — independent of this repo), the server URL, how memory is wired (HTTP hooks vs MCP tools — recall is auto-injected on UserPromptSubmit, not a tool the model calls; writes happen on configured hook events), confirmation that it is enabled globally, and the fact that I just run claude (or claude -p) with nothing extra. Note: the background server does not survive a reboot — offer to set up a systemd user service for autostart.

Re-running / refreshing later

This file is safe to re-run end to end. The only manual-feeling case is refreshing the cached plugin after you change the plugin or rebuild the Go hooks at the SAME version: rebuild (make build-claude-hook), re-copy the result into the staged marketplace (rsync -a --delete <ABS_PATH>/apps/claude-code-plugin/ ~/.claude/marketplaces/powermem/), then force-refresh the cache with claude plugin uninstall memory-powermem@powermem followed by claude plugin install memory-powermem@powermem --scope user (or bump the version in .claude-plugin/plugin.json so claude plugin update memory-powermem picks it up).

To turn it off without uninstalling: claude plugin disable memory-powermem@powermem (re-enable with claude plugin enable ...). To disable only prompt-time search injection, set POWERMEM_PROMPT_SEARCH=0. The hook talks to POWERMEM_BASE_URL (default http://localhost:8848).

For the full manual reference, see ../../docs/integrations/claude_code.md

🚨 COMPREHENSIVE ERROR HANDLING GUIDE

Every real-world setup encounters issues. This guide documents specific error scenarios and their resolutions discovered during actual setup attempts:

Log File Locations

  • powermem-server: server.log (RotatingFileHandler, 10MB max, 5 backups)
  • seekdb: seekdb_data/log/seekdb.log (native C++ engine log)

Error Resolution Checklist

[E001] PEP 668 System Protection

Problem: system Python blocks package installation with "externally-managed-environment" Fix: use uv with a virtual environment

uv venv venv --python python3.11
source venv/bin/activate
# Use the extras matching your storage backend:
#   SQLite (default):  .[server,extras]   (pulls sentence-transformers)
#   OceanBase:         .[server,seekdb]
uv pip install --python "$VIRTUAL_ENV/bin/python" -e '.[server,extras]'

[E002] Missing Server Dependencies

Problem: Server startup fails with missing packages Fix: Install missing dependencies with the extras matching your backend

# SQLite (default)
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,extras]'
# OceanBase
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,seekdb]'

[E003] SeekDB File Locking

Problem: "open seekdb failed OB_ERROR(4000)" or "db opened by other process" Fix: Stop duplicate servers, then remove stale seekdb data only if data loss is acceptable

pkill -f powermem-server
rm -rf seekdb_data
powermem-server --host 0.0.0.0 --port 8848 &

[E004] Missing Go for Hooks

Problem: make build-claude-hook fails Fix: Install Go 1.22+

brew install go
make build-claude-hook

[E005] Storage Backend Initialization

Problem: 503 errors on API calls despite server health Fix: the Claude Code plugin defaults to local SQLite. Stop the managed server, remove stale SQLite data only if you accept deleting local memories, and restart init:

sh "$CLAUDE_PLUGIN_ROOT/scripts/stop.sh"
rm -f "$HOME/.powermem/powermem.db" "$HOME/.powermem/powermem.db-"*
sh "$CLAUDE_PLUGIN_ROOT/scripts/init.sh"

If you explicitly set POWERMEM_INIT_DATABASE_PROVIDER=oceanbase, use the OceanBase/seekdb troubleshooting path instead and remove seekdb_data only when data loss is acceptable.

[E006] Model Download Timeout

Problem: Server hangs or reports "timed out thrown while requesting HEAD" on startup. Cause: The embedding model is not cached and the network is unreachable. Fix: Follow the model pre-download step in Step 3a (branch on region detected in Step 1a). Quick reference:

CN region (ModelScope → HF hub bridge):

# Detect the correct interpreter first (same one powermem uses):
POWERMEM_PYTHON=$(head -1 "$(command -v powermem-server)" | sed 's|#!||;s| .*||')
uv pip install --python "$POWERMEM_PYTHON" modelscope
$POWERMEM_PYTHON -c "from modelscope import snapshot_download; \
                     snapshot_download('AI-ModelScope/all-MiniLM-L6-v2')"
# Verify (note: models/ subdirectory is required):
ls ~/.cache/modelscope/hub/models/AI-ModelScope/all-MiniLM-L6-v2/

Non-CN region (HuggingFace direct):

POWERMEM_PYTHON=$(head -1 "$(command -v powermem-server)" | sed 's|#!||;s| .*||')
uv pip install --python "$POWERMEM_PYTHON" huggingface_hub
$POWERMEM_PYTHON -c "from huggingface_hub import snapshot_download; \
                     snapshot_download('sentence-transformers/all-MiniLM-L6-v2')"
# Verify:
ls ~/.cache/huggingface/hub/models--sentence-transformers--all-MiniLM-L6-v2/snapshots/

⚠️ Do NOT use bare python here — on many systems the default python version is below 3.11. Using the shebang from powermem-server guarantees all three steps (uv install, download, bridge) run in the same environment. Then run the bridge script from Step 3a to populate the HuggingFace hub cache structure — the embedder's cache-detection function checks ~/.cache/huggingface/hub/, not the ModelScope layout.

⚠️ DO NOT use sentence_transformers.SentenceTransformer(...) to download — it can hang on networks where HuggingFace is unreachable. On CN region, use ModelScope + HF hub bridge. On non-CN region, use huggingface_hub.snapshot_download. Always check region first (Step 1a).

To confirm which sources are reachable:

curl -s -m 10 -o /dev/null -w "ModelScope: HTTP %{http_code}\n" \
  https://www.modelscope.cn/api/v1/models/AI-ModelScope/all-MiniLM-L6-v2
curl -s -m 10 -o /dev/null -w "HuggingFace: HTTP %{http_code}\n" \
  https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2

[E008] curl Exit Code 7 After Server Start

Problem: curl returns exit code 7 ("Failed to connect") right after launching powermem-server. Cause: The server process is running but not yet listening on port 8848. First launch can take 60–120s (local embedder load/download + uvicorn bind). Fix: Use the polling loop from Step 3a. Do NOT kill and restart — restarting resets initialization and makes startup take even longer.

[E009] Server Exits Immediately — fastapi, uvicorn, or fastmcp Missing

Problem: Server exits immediately after launch with "Missing dependencies" error. Cause: installing only the base project skips optional server dependencies. powermem[server] installs fastapi, uvicorn, and fastmcp for the HTTP API plus MCP transports. Fix:

uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'

[E010] Anthropic temperature + top_p both sent → 400

Problem: Memory writes return success:true but data:[]; server.log reports Error code: 400 - temperature and top_p cannot both be specified for this model. Cause: base.py._get_common_params populates both temperature and top_p by default. The Anthropic API (and most Anthropic-compatible proxies) rejects requests that include both parameters simultaneously. Fix: In src/powermem/integrations/llm/anthropic.py, drop top_p from params before calling the API:

params = self._get_supported_params(messages=messages, **kwargs)
params.pop("top_p", None)   # Anthropic rejects requests with both temperature and top_p

[E007] Claude MCP Server Shows Failed

Problem: claude mcp list shows powermem as failed or disconnected. Fix: Inspect Claude's registered command, then run the MCP server directly and check stdout/stderr separately:

claude mcp list
claude mcp get powermem
command -v powermem-mcp
powermem-mcp --help

powermem-mcp stdio >/tmp/powermem-mcp.stdout 2>/tmp/powermem-mcp.stderr &
MCP_PID=$!; sleep 10; kill "$MCP_PID" 2>/dev/null || true; wait "$MCP_PID" 2>/dev/null || true
sed -n '1,120p' /tmp/powermem-mcp.stderr
sed -n '1,40p' /tmp/powermem-mcp.stdout

Diagnosis:

  • Import errors in stderr mean the package or virtual environment is broken.
  • Missing key/config errors mean .env must be corrected after user approval.
  • Network/model timeout errors mean the embedder or model cache needs attention.
  • Any banner, warning, stack trace, or non-JSON text in stdout breaks MCP stdio.
  • Empty stdout/stderr after 10 seconds usually means the process started and is waiting for JSON-RPC input; re-check Claude-side registration next.

[E011] Python Version Below 3.11

Problem: package installation fails or venv created with wrong Python version. PowerMem requires Python >= 3.11 (pyproject.toml: requires-python = ">=3.11"). Fix: Verify and use Python 3.11+:

python3 --version  # must be >= 3.11
# If too old, find and use a newer Python:
python3.11 --version
uv venv venv --python python3.11
source venv/bin/activate

[E012] uv Missing or Not on PATH

Problem: uv: command not found Fix: Install uv for your region, then add the install directory to PATH:

# Non-CN:
curl -LsSf https://astral.sh/uv/install.sh | sh

# CN:
export UV_DOWNLOAD_URL=https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/
curl -sL https://mirrors.ustc.edu.cn/github-release/astral-sh/uv/LatestRelease/uv-installer.sh | sh

export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
uv --version

[E013] Internal PyPI Mirror Missing Packages

Problem: uv pip install fails with "No matching distribution found for pyobvector" (or pyseekdb, onnxruntime, etc.). Cause: The configured Python package index (e.g. internal mirror at yum.tbsite.net) does not mirror all required packages. Fix: If the package is already installed elsewhere (e.g. system Python 3.11), copy it into the venv:

source venv/bin/activate
SITE_PKGS=$(python -c "import site; print(site.getsitepackages()[0])")
# Find the missing package in another Python installation:
find ~/.local/lib/python3.11/site-packages -maxdepth 1 -name "pyobvector*" -type d
# Copy both the package and its .dist-info:
cp -r ~/.local/lib/python3.11/site-packages/pyobvector "$SITE_PKGS/"
cp -r ~/.local/lib/python3.11/site-packages/pyobvector-*.dist-info "$SITE_PKGS/"

⚠️ After copying packages manually, you must also install their transitive dependencies — see [E014].

[E014] Missing Transitive Dependencies After Manual Copy

Problem: Server starts but returns "Memory service unavailable: storage backend initialization failed" with ModuleNotFoundError: No module named 'onnxruntime' (or tenacity, tokenizers). Cause: When packages like pyobvector/pyseekdb are copied manually into the venv, their transitive dependencies (onnxruntime, tenacity, tokenizers, etc.) are not automatically resolved by the package installer. Fix: Install the missing transitive dependencies:

source venv/bin/activate
uv pip install --python "$VIRTUAL_ENV/bin/python" onnxruntime tenacity tokenizers

To verify no other dependencies are missing after manual copying:

python -c "import pyobvector" 2>&1   # should produce no output
python -c "import pyseekdb" 2>&1    # should produce no output

PRE-CHECK & PREREQUISITES

  1. Verify Python version: python3 --version (must be >= 3.11, see [E011])
  2. Verify uv: uv --version (install it with [E012] if missing)
  3. Verify hook binaries: committed hooks/bin/ binaries should already be present; Go 1.22+ is only needed when refreshing them from hook source changes.
  4. Check mirror access: if using an internal mirror, verify it has pyobvector, pyseekdb, and onnxruntime; see [E013] if not.

STEP BY STEP PROVEN PATH

Given the encountered errors, here are the tested workarounds for each approach:

Method A: SOURCE Path (Current Directory Build)

# Create virtual environment to avoid PEP 668 issues.
uv venv venv --python python3.11
source venv/bin/activate
POWERMEM_PYTHON="$VIRTUAL_ENV/bin/python"

# Install everything with ALL required extras
uv pip install --python "$POWERMEM_PYTHON" -e '.[server,seekdb]'

# Git/marketplace installs use committed hook binaries.
# Optional after hook source changes:
# make build-claude-hook

# Register marketplace
DEST="$HOME/.claude/marketplaces/powermem"
mkdir -p "$DEST"
rsync -a --delete "$(pwd)/apps/claude-code-plugin/" "$DEST/"
claude plugin marketplace add "$DEST"
claude plugin install memory-powermem@powermem --scope user

# Start server (logs go to server.log automatically)
powermem-server --host 0.0.0.0 --port 8848 &
# Clean virtual environment approach
uv venv venv --python python3.11
source venv/bin/activate
POWERMEM_PYTHON="$VIRTUAL_ENV/bin/python"
uv pip install --python "$POWERMEM_PYTHON" 'powermem[server,seekdb]'
claude mcp remove powermem 2>/dev/null
claude mcp add powermem -- powermem-mcp stdio

Method C: Troubleshooting Installation

# Common troubleshooting commands
lsof -i :8848    # Check if port is in use
pkill -f powermem-server  # Kill any running server
rm -rf seekdb_data  # Reset SeekDB if corrupted and data loss is acceptable

# Check logs
tail -f server.log                    # PowerMem server errors
tail -f seekdb_data/log/seekdb.log    # SeekDB engine errors

FINAL VALIDATION STEPS

After setup, verify the complete round-trip:

# 1. Health check
curl -s http://localhost:8848/api/v1/system/health

# 2. Write test memory
curl -s -X POST http://localhost:8848/api/v1/memories \
  -H 'Content-Type: application/json' \
  -d '{"content":"PowerMem setup verification complete","user_id":"setup_test"}'

# 3. Search test
curl -s -X POST http://localhost:8848/api/v1/memories/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"setup verification","user_id":"setup_test","limit":1}'

SYSTEMD AUTOSTART (Optional)

mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/powermem.service << EOF
[Unit]
Description=PowerMem Memory Server
After=network.target

[Service]
Type=simple
WorkingDirectory=$(pwd)
ExecStart=/bin/bash -c 'source venv/bin/activate && powermem-server --host 0.0.0.0 --port 8848'
Restart=always
RestartSec=5

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable powermem.service
systemctl --user start powermem.service

SUMMARY

Path taken: [Based on observed errors, recommend PYPI/MCP approach for stability]

  • .env location: $(pwd)/.env
  • Virtual environment: $(pwd)/venv
  • Plugin marketplace: ~/.claude/marketplaces/powermem
  • Server URL: http://localhost:8848
  • Memory system: seekdb storage with HTTP hooks
  • Global enablement: Complete via claude plugin install
  • Usage: Run claude command with no extra flags needed

Quick commands for daily use:

# Start server
source venv/bin/activate
powermem-server --host 0.0.0.0 --port 8848

# Check status
systemctl --user status powermem.service

# Quick restart
ps aux | grep powrmem

Your Claude Code is now configured with automatic memory recall and persistence worldwide.