DeepSeek Harness (DSH)? This wizard installs the MCP server for Claude Code / Cursor / OpenCode. For DSH, skip this file entirely and follow
packages/dsh/install.md— the native plugin needs no agent-driven install (and no database server under the default embedded store).
STOP. Read this entire file before running ANY commands. This is a 3-phase install. You MUST complete each phase in order. Do NOT skip ahead. Do NOT run npm install, npm run build, claude mcp add, or any other commands until instructed. Phase 2 requires asking the user questions — do NOT assume defaults.
Run this command now and read the output carefully:
node --version && npm --version && curl -sf http://localhost:8000/api/v2/heartbeat && whoami && ls packages/mcp/dist/index.js 2>/dev/null; ls ~/.claude/ 2>/dev/null && ls ~/.cursor/ 2>/dev/null && ls .opencode/ 2>/dev/null
STOP. Do not run npm install, npm run build, or any other install command yet. Read the output and proceed to Phase 2.
You MUST use the AskUserQuestion tool here. Do NOT skip this phase. Do NOT ask questions as plain text. Combine all applicable questions into one AskUserQuestion call with selectable options:
- If
node --versionfailed or printed < 18: question "How should Node.js be installed?" with options: "nvm", "System package manager" - If
curlto ChromaDB failed: question "How should ChromaDB be installed?" with options: "Docker (recommended)", "pip", "NixOS" - If neither
~/.claude/,~/.cursor/, nor.opencode/was found: question "Which editor are you using?" with options: "Claude Code", "Cursor", "OpenCode" - Always ask: "Username for task IDs?" with options: the output of
whoami(labeled with the actual username), "Custom username" - Always ask: "Where should YAPA instructions be installed?" — first option MUST be "This project only", second option "Globally for all sessions". Do not reorder these options.
- Always ask: "Enable remote syncing?" with options: "No", "Yes — new database", "Yes — existing database". If the user selects "Yes — existing database", ask for their PostgreSQL connection URL (e.g.,
postgres://user:pass@host:5432/yapa). If the user selects "Yes — new database", ask a follow-up: "How would you like to host PostgreSQL?" with options: "Docker (local)", "Managed service (Neon, Supabase)", "Cloud provider (AWS, GCP, Azure)". If "Managed service": ask "Which service?" with options: "Neon (free serverless)", "Supabase (free hosted)". If "Cloud provider": ask "Which provider?" with options: "AWS (RDS)", "GCP (Cloud SQL)", "Azure (Flexible Server)". Do NOT ask for a connection URL yet — Phase 3 will handle setup.
STOP. Wait for the user's answers before proceeding. You need USERNAME, SCOPE, and either SYNC_DATABASE_URL (if "existing database") or SYNC_PROVIDER (if "new database" — one of: docker, neon, supabase, aws, gcp, azure) from these answers for Phase 3.
Now execute these steps in order, without asking anything else:
-
Move this repo to the correct location based on the user's SCOPE from Phase 2:
- If scope=global:
~/.local/share/yapa/ - If scope=project:
.yapa/in the user's current working directory (the directory they were in when they asked to install, NOT the repo directory)
If the repo is already in the correct location, skip this step. Otherwise:
mkdir -p TARGET_LOCATION && mv THIS_REPO_DIR TARGET_LOCATIONThen
cd TARGET_LOCATIONand continue from there. All subsequent steps use this as the repo directory. - If scope=global:
-
If Node.js needed: install using the user's chosen method, then verify
node --version>= 18. -
Run:
cd THIS_REPO_DIR && npm install && npm run buildVerify:ls packages/mcp/dist/index.jssucceeds. -
If ChromaDB needed, start it using the user's chosen method:
- Docker:
docker run -d --name chromadb --restart unless-stopped -p 8000:8000 -v chromadb_data:/data chromadb/chroma - pip:
pip install chromadb && chroma run --host 0.0.0.0 --port 8000 & - NixOS: tell user to add
services.chromadb.enable = true;and rebuild Verify:curl -sf http://localhost:8000/api/v2/heartbeatsucceeds.
- Docker:
-
If user chose "Yes — new database" for remote syncing, set up PostgreSQL+pgvector using the provider chosen in Phase 2:
Docker (local):
Check if port 5432 is available:
ss -tln | grep :5432If port 5432 is in use, use 5433 instead. Set PG_PORT accordingly (default 5432).
Generate a random password and start the container:
YAPA_PG_PASS=$(openssl rand -hex 16) docker run -d --name yapa-postgres --restart unless-stopped \ -p ${PG_PORT}:5432 \ -e POSTGRES_DB=yapa \ -e POSTGRES_USER=yapa \ -e POSTGRES_PASSWORD=${YAPA_PG_PASS} \ -v yapa_pgdata:/var/lib/postgresql/data \ pgvector/pgvector:pg17Wait for PostgreSQL to be ready (up to 30 seconds):
for i in $(seq 1 30); do docker exec yapa-postgres pg_isready -U yapa -d yapa >/dev/null 2>&1 && break || sleep 1; doneVerify:
docker exec yapa-postgres psql -U yapa -d yapa -c "SELECT 1"Set:
SYNC_DATABASE_URL="postgres://yapa:${YAPA_PG_PASS}@localhost:${PG_PORT}/yapa"Neon:
Tell the user:
- Go to https://console.neon.tech and sign up (free tier includes 0.5 GB storage)
- Create a new project (any name, e.g. "yapa")
- On the project dashboard, copy the connection string shown
- Neon includes pgvector by default — no extra setup needed
Ask the user to paste the connection string. Set:
SYNC_DATABASE_URL=<pasted URL>Supabase:
Tell the user:
- Go to https://supabase.com/dashboard and sign up (free tier includes 500 MB)
- Create a new project (any name, e.g. "yapa") — choose a database password
- Go to Settings → Database → Connection string → URI, copy it (replace
[YOUR-PASSWORD]with your database password) - Supabase includes pgvector by default — no extra setup needed
Ask the user to paste the connection string. Set:
SYNC_DATABASE_URL=<pasted URL>AWS (RDS):
Check if
awsCLI is installed and authenticated:aws sts get-caller-identity 2>/dev/nullIf the CLI is available and authenticated, offer to provision automatically. If the user accepts:
YAPA_PG_PASS=$(openssl rand -hex 16) aws rds create-db-instance \ --db-instance-identifier yapa \ --db-instance-class db.t3.micro \ --engine postgres \ --engine-version 17 \ --master-username yapa \ --master-user-password "${YAPA_PG_PASS}" \ --allocated-storage 20 \ --publicly-accessible \ --no-multi-azWait for the instance to become available (this can take several minutes):
aws rds wait db-instance-available --db-instance-identifier yapaGet the endpoint:
RDS_ENDPOINT=$(aws rds describe-db-instances --db-instance-identifier yapa --query 'DBInstances[0].Endpoint.Address' --output text)Create the yapa database and enable pgvector:
PGPASSWORD="${YAPA_PG_PASS}" psql -h "${RDS_ENDPOINT}" -U yapa -d postgres -c "CREATE DATABASE yapa;" PGPASSWORD="${YAPA_PG_PASS}" psql -h "${RDS_ENDPOINT}" -U yapa -d yapa -c "CREATE EXTENSION IF NOT EXISTS vector;"Set:
SYNC_DATABASE_URL="postgres://yapa:${YAPA_PG_PASS}@${RDS_ENDPOINT}:5432/yapa"If the CLI is NOT available or the user declines automatic setup, tell the user:
- Go to the RDS console at https://console.aws.amazon.com/rds/
- Click "Create database" → choose PostgreSQL, version 15+
- Choose "Free tier" template if available, or the smallest instance (db.t3.micro)
- Set DB identifier:
yapa, master username:yapa, choose a password - Under Connectivity, ensure "Public access" is set to Yes (or configure VPC peering as needed)
- Create the database and wait for it to become "Available"
- Copy the endpoint from the database details page
- Connect and enable pgvector:
psql -h <endpoint> -U yapa -d postgres -c "CREATE EXTENSION IF NOT EXISTS vector;" - Create the yapa database:
CREATE DATABASE yapa;
Ask the user to provide the connection string. Set:
SYNC_DATABASE_URL="postgres://yapa:<password>@<endpoint>:5432/yapa"GCP (Cloud SQL):
Check if
gcloudCLI is installed and authenticated:gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/nullIf the CLI is available and authenticated, offer to provision automatically. If the user accepts:
YAPA_PG_PASS=$(openssl rand -hex 16) GCP_PROJECT=$(gcloud config get-value project) gcloud sql instances create yapa \ --database-version=POSTGRES_17 \ --tier=db-f1-micro \ --region=us-central1 \ --assign-ip \ --project="${GCP_PROJECT}"Set the root password, create user and database:
gcloud sql users set-password postgres --instance=yapa --password="${YAPA_PG_PASS}" --project="${GCP_PROJECT}" gcloud sql users create yapa --instance=yapa --password="${YAPA_PG_PASS}" --project="${GCP_PROJECT}" gcloud sql databases create yapa --instance=yapa --project="${GCP_PROJECT}"Authorize the current IP and get the instance IP:
MY_IP=$(curl -sf ifconfig.me) gcloud sql instances patch yapa --authorized-networks="${MY_IP}/32" --project="${GCP_PROJECT}" CSQL_IP=$(gcloud sql instances describe yapa --format="value(ipAddresses[0].ipAddress)" --project="${GCP_PROJECT}")Enable pgvector:
PGPASSWORD="${YAPA_PG_PASS}" psql -h "${CSQL_IP}" -U yapa -d yapa -c "CREATE EXTENSION IF NOT EXISTS vector;"Set:
SYNC_DATABASE_URL="postgres://yapa:${YAPA_PG_PASS}@${CSQL_IP}:5432/yapa"If the CLI is NOT available or the user declines automatic setup, tell the user:
- Go to https://console.cloud.google.com/sql
- Click "Create instance" → choose PostgreSQL, version 15+
- Set instance ID:
yapa, choose a root password, pick a region - Choose the smallest machine type (Shared core, 1 vCPU)
- Under Connections, enable "Public IP" and add your IP to authorized networks (or use Cloud SQL Auth Proxy)
- Once created, click the instance → "Databases" tab → "Create database" named
yapa - Create a user: "Users" tab → "Add user account" → username
yapa, choose a password - pgvector is supported on Cloud SQL — enable it by connecting and running:
CREATE EXTENSION IF NOT EXISTS vector;
Ask the user to provide the connection string. Set:
SYNC_DATABASE_URL="postgres://yapa:<password>@<public-ip>:5432/yapa"Azure (Flexible Server):
Check if
azCLI is installed and authenticated:az account show 2>/dev/nullIf the CLI is available and authenticated, offer to provision automatically. If the user accepts:
YAPA_PG_PASS=$(openssl rand -hex 16) AZ_RESOURCE_GROUP="yapa-rg" AZ_REGION="eastus" az group create --name "${AZ_RESOURCE_GROUP}" --location "${AZ_REGION}" az postgres flexible-server create \ --resource-group "${AZ_RESOURCE_GROUP}" \ --name yapa \ --location "${AZ_REGION}" \ --admin-user yapa \ --admin-password "${YAPA_PG_PASS}" \ --sku-name Standard_B1ms \ --tier Burstable \ --public-access 0.0.0.0 \ --yesCreate the database and enable pgvector:
az postgres flexible-server db create --resource-group "${AZ_RESOURCE_GROUP}" --server-name yapa --database-name yapa az postgres flexible-server parameter set --resource-group "${AZ_RESOURCE_GROUP}" --server-name yapa --name azure.extensions --value VECTOR PGPASSWORD="${YAPA_PG_PASS}" psql -h yapa.postgres.database.azure.com -U yapa -d yapa -c "CREATE EXTENSION IF NOT EXISTS vector;"Set:
SYNC_DATABASE_URL="postgres://yapa:${YAPA_PG_PASS}@yapa.postgres.database.azure.com:5432/yapa"If the CLI is NOT available or the user declines automatic setup, tell the user:
- Go to https://portal.azure.com → search "Azure Database for PostgreSQL"
- Click "Create" → choose "Flexible Server"
- Set server name:
yapa, choose admin usernameyapaand a password, pick a region - Choose the Burstable tier (B1ms) for lowest cost
- Under Networking, enable "Allow public access" and add your client IP
- Once deployed, go to "Server parameters" → search
azure.extensions→ enableVECTOR - Connect and create the database:
CREATE DATABASE yapa;thenCREATE EXTENSION IF NOT EXISTS vector;
Ask the user to provide the connection string. Set:
SYNC_DATABASE_URL="postgres://yapa:<password>@<server-name>.postgres.database.azure.com:5432/yapa"For all providers, after obtaining SYNC_DATABASE_URL, tell the user:
Save this connection URL — you'll need it if you reinstall YAPA or add another machine. Share it with teammates who want to sync with you.
-
Register the YAPA MCP server, replacing ABSOLUTE_PATH with this repo's absolute path and USERNAME with the user's answer from Phase 2. If the user enabled remote syncing, also add the sync env vars shown below:
Claude Code — run:
claude mcp add -e YAPA_USERNAME=USERNAME -s user yapa -- node ABSOLUTE_PATH/packages/mcp/dist/index.jsIf sync enabled, also add:
-e YAPA_SYNC_ENABLED=true -e YAPA_SYNC_DATABASE_URL=SYNC_DATABASE_URLCursor — merge into
mcpServersin~/.cursor/mcp.json:{ "mcpServers": { "yapa": { "command": "node", "args": ["ABSOLUTE_PATH/packages/mcp/dist/index.js"], "env": { "YAPA_USERNAME": "USERNAME", "YAPA_SYNC_ENABLED": "true", "YAPA_SYNC_DATABASE_URL": "SYNC_DATABASE_URL" } } } }Omit the
YAPA_SYNC_*keys if sync is not enabled.OpenCode — merge into
mcpServersin.opencode/config.json:{ "mcpServers": { "yapa": { "command": "node", "args": ["ABSOLUTE_PATH/packages/mcp/dist/index.js"], "env": { "YAPA_USERNAME": "USERNAME", "YAPA_SYNC_ENABLED": "true", "YAPA_SYNC_DATABASE_URL": "SYNC_DATABASE_URL" } } } }Omit the
YAPA_SYNC_*keys if sync is not enabled. -
Write the YAPA behavioral instructions. Append the following block to the correct file based on the user's SCOPE and editor choice from Phase 2:
- If scope=project and Claude Code: append to
CLAUDE.mdin the user's current working directory - If scope=project and OpenCode: append to
AGENTS.mdin the user's current working directory - If scope=global and Claude Code: append to
~/.claude/CLAUDE.md - If scope=global and OpenCode: append to
~/.config/opencode/AGENTS.md
Append this exact block:
## YAPA — Memory & Task Assistant You have access to YAPA memory and task tools via MCP. Follow these rules: ### ChromaDB Prerequisite Before using any YAPA tool, verify ChromaDB is reachable: 1. Run: `curl -sf http://localhost:8000/api/v2/heartbeat` 2. If it succeeds, ChromaDB is running — proceed normally. 3. If it fails, tell the user: > "YAPA needs ChromaDB to store memories and tasks, but it doesn't appear to be running locally. How would you like to install it?" Then offer these options (Docker is recommended): - **Docker (recommended):** `docker run -d --name chromadb --restart unless-stopped -p 8000:8000 -v chromadb_data:/data chromadb/chroma` - **pip:** `pip install chromadb && chroma run --host 0.0.0.0 --port 8000` - **NixOS service:** add `services.chromadb.enable = true;` to your NixOS config 4. After the user installs and starts ChromaDB, re-check the heartbeat before continuing. 5. Do NOT silently swallow connection errors from YAPA tools — if ChromaDB goes down mid-session, notify the user immediately. ### First Sync Check At the start of each session, if sync tools are available, call `sync_status`. If last pull shows "never", tell the user: > "YAPA sync is enabled but hasn't synced yet. Would you like me to sync now?" ### Per-Prompt Routine (run on EVERY user message) Run these steps in order, in parallel where possible, before drafting your response: 1. **Detect scope** — infer the project/customer from the prompt + cwd. Pick the collection (`customer-{name}`, `project-{name}`, or `global`). If genuinely ambiguous, ask the user once and remember the answer for the session. 2. **Recall** — call `memory_recall` against the detected collection with a semantic query derived from the prompt. 3. **Task check** — call `task_list` filtered to the detected collection at session start AND whenever the detected scope changes. Skip on subsequent prompts within the same scope (rely on in-conversation state instead). 4. **Ensure a task exists for the active work** — call `task_create` in the detected collection BEFORE starting work when the prompt implies multi-step work, work that will take time, or work that will outlive the current turn (investigations, code changes, bug fixes, meeting prep, ongoing initiatives). Skip for one-shot questions, lookups, or single-turn answers. ### Auto-Capture During Work Capture as soon as the trigger fires — do NOT batch to the end of the session. **Triggers that create a memory (salience >= 2.0):** - A bug's root cause is identified - A configuration value, env var, endpoint, or credential location is learned - The user states a preference, decision, or correction - A non-obvious technical fact about the codebase/customer/cluster is discovered - A solution that took non-trivial effort to find (likely to recur) - A meeting or Slack thread surfaces a decision or commitment **Triggers that create a task (in addition to any memory):** - A follow-up action is identified ("we should also...", "TODO", "next time...") - The user asks for something that can't be finished this turn - A blocker is discovered (also: `task_update` the parent task to "blocked") - An action item shows up in a customer meeting or Slack thread **Contradiction check:** `memory_store` returns a `potential_conflicts` field listing near-duplicate memories in the same collection. Read it — if a conflict exists, decide supersede (re-store with `supersedes: "<old ID>"` — archives the old memory, recoverable via include_archived) or coexist (proceed as-is) BEFORE moving on. Reserve `memory_forget` for memories that should never have existed. **Do not store:** ephemeral conversation state, obvious code patterns, anything already captured in git history or in an existing memory. ### End-of-Session Journal - During the session, call `journal_append` with a one-line note whenever a meaningful step completes (decision made, finding confirmed, task closed). - Before the session ends — or when prompted by the SessionEnd hook — call `journal_consolidate` to merge the drafts into a single memory tagged `journal`. The next session's recall will surface it. ### Task Management Lifecycle - On completion: `task_complete` - On blocker: `task_update` with status `blocked` and a note explaining the block - On scope change: `task_update` to amend the description rather than creating a duplicate - Session start: covered by Per-Prompt Routine step 3 ### Periodic Compaction - When YAPA emits a "compaction candidate" reminder at session start, call `compaction_suggest` for the named collection, write a rolling summary of each group, and submit it via `compaction_apply`. Originals get archived (filtered from recall by default). ### Collection Inference - Infer the appropriate collection from conversation context: - Customer name mentioned → `customer-{name}` - Project-specific work → `project-{name}` or `customer-{name}` - General/cross-cutting knowledge → `global` - Private/personal data → `private-{name}` or `local-{name}` (these do NOT sync to remote) - **Before creating a new collection**, ask the user to confirm the collection name. Suggest a name based on context. Remind the user that `private-` or `local-` prefixed collections won't sync to the shared remote database. - When unsure which collection to use, ask the user - Use `collection_list` to check what collections exist before creating new ones - Always pass the inferred collection explicitly on memory/task tool calls ### Uninstall If the user says "uninstall yapa": 1. Use AskUserQuestion to ask "Do you want to keep your ChromaDB collections (memories and tasks)?" with exactly two options: "Yes" (first), "No". Do not ask this as plain text. 2. Call the `uninstall` MCP tool with `delete_collections: true` if the user answered "No", or `delete_collections: false` if "Yes". 3. Execute every step the tool returns without asking anything else. - If scope=project and Claude Code: append to
-
Tell the user: "YAPA is installed. Restart your editor to activate it. To uninstall later, say 'uninstall yapa' in any session." If sync was enabled, also tell the user: "After restarting, say 'sync yapa' to push your local data to the remote database and pull any shared data."