#!/bin/bash # Trapic one-click manual install — MCP server + SessionStart hook # Usage: curl -fsSL https://raw.githubusercontent.com/nickjazz/trapic-plugin/main/scripts/install.sh | bash set -e GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' echo "" echo -e "${BLUE}=== Trapic Install ===${NC}" echo "" # ── 0. Mode selection ───────────────────────────────────────────────── # Detect mode: cloud (default) or self-hosted MODE="cloud" MCP_URL="https://mcp.trapic.ai/mcp" if [ -n "$TRAPIC_URL" ]; then MODE="self-hosted" MCP_URL="$TRAPIC_URL" echo -e "${GREEN}✓${NC} Self-hosted mode: $MCP_URL" elif [ -n "$1" ] && [ "$1" = "--self-hosted" ]; then MODE="self-hosted" read -p " Enter your Trapic server URL (e.g., http://localhost:1888/mcp): " MCP_URL < /dev/tty echo -e "${GREEN}✓${NC} Self-hosted mode: $MCP_URL" else echo " Mode: Cloud (trapic.ai)" echo " For self-hosted: curl ... | bash -s -- --self-hosted" echo " Or: TRAPIC_URL=http://localhost:1888/mcp bash install.sh" echo "" fi # ── 1. Token ────────────────────────────────────────────────────────── SETTINGS="$HOME/.claude/settings.json" mkdir -p "$HOME/.claude" if [ "$MODE" = "self-hosted" ]; then # Self-hosted: check if server needs a token echo -e "${YELLOW}!${NC} Checking if your server requires authentication..." HEALTH=$(curl -sf "${MCP_URL%/mcp}/health" 2>/dev/null || echo "") if [ -n "$HEALTH" ]; then echo -e "${GREEN}✓${NC} Server is reachable" else echo -e "${YELLOW}!${NC} Server not reachable at ${MCP_URL%/mcp}/health — make sure it's running" fi # Self-hosted might not need a token (open mode) if [ -n "$TRAPIC_TOKEN" ]; then TOKEN="$TRAPIC_TOKEN" echo -e "${GREEN}✓${NC} Using TRAPIC_TOKEN: ${TOKEN:0:6}..." else read -p " API key (leave empty if server is in open mode): " TOKEN < /dev/tty [ -z "$TOKEN" ] && echo -e "${GREEN}✓${NC} No token — using open mode" fi else # Cloud mode: token required if [ -n "$TRAPIC_TOKEN" ]; then TOKEN="$TRAPIC_TOKEN" echo -e "${GREEN}✓${NC} Using TRAPIC_TOKEN from environment: ${TOKEN:0:6}..." elif [ -f "$SETTINGS" ] && python3 -c "import json; t=json.load(open('$SETTINGS')).get('env',{}).get('TRAPIC_TOKEN',''); exit(0 if t and t!='PASTE_YOUR_TOKEN_HERE' else 1)" 2>/dev/null; then TOKEN=$(python3 -c "import json; print(json.load(open('$SETTINGS')).get('env',{}).get('TRAPIC_TOKEN',''))") echo -e "${GREEN}✓${NC} Found token in settings.json: ${TOKEN:0:6}..." else echo -e "${YELLOW}!${NC} No TRAPIC_TOKEN found." echo "" echo " Get your token at https://trapic.ai" echo "" read -p " Paste your token (tr_...): " TOKEN < /dev/tty if [ -z "$TOKEN" ]; then echo " Skipped. You can add it later to $SETTINGS" TOKEN="" fi fi fi # Write token to settings.json if [ -n "$TOKEN" ]; then python3 -c " import json, os p = os.path.expanduser('$SETTINGS') d = json.load(open(p)) if os.path.exists(p) else {} d.setdefault('env', {})['TRAPIC_TOKEN'] = '$TOKEN' json.dump(d, open(p, 'w'), indent=2) " 2>/dev/null echo -e "${GREEN}✓${NC} Token saved to $SETTINGS" fi # ── 2. MCP server ──────────────────────────────────────────────────── # Check project-level .mcp.json first, then user-level ~/.claude.json MCP_FILE=".mcp.json" if [ ! -d ".git" ]; then MCP_FILE="$HOME/.claude.json" fi if [ -f "$MCP_FILE" ] && grep -q "trapic" "$MCP_FILE" 2>/dev/null; then echo -e "${GREEN}✓${NC} MCP server already configured in $MCP_FILE" else # Build MCP server config based on mode if [ "$MODE" = "self-hosted" ]; then # Self-hosted: use HTTP transport (core supports Streamable HTTP + GET discovery) MCP_TYPE="http" if [ -n "$TOKEN" ]; then MCP_HEADERS="'headers': { 'Authorization': 'Bearer ${TOKEN}' }," MCP_HEADERS_JSON="\"headers\": { \"Authorization\": \"Bearer ${TOKEN}\" }," else MCP_HEADERS="" MCP_HEADERS_JSON="" fi else # Cloud: use http transport (supports OAuth) MCP_TYPE="http" MCP_HEADERS="'headers': { 'Authorization': 'Bearer \${TRAPIC_TOKEN}' }," MCP_HEADERS_JSON="\"headers\": { \"Authorization\": \"Bearer \${TRAPIC_TOKEN}\" }," fi if [ -f "$MCP_FILE" ]; then python3 -c " import json p = '$MCP_FILE' d = json.load(open(p)) cfg = { 'type': '${MCP_TYPE}', 'url': '${MCP_URL}' } token_str = '${TOKEN}' mode = '${MODE}' if mode == 'self-hosted' and token_str: cfg['headers'] = { 'Authorization': 'Bearer ' + token_str } elif mode == 'cloud': cfg['headers'] = { 'Authorization': 'Bearer \${TRAPIC_TOKEN}' } d.setdefault('mcpServers', {})['trapic'] = cfg json.dump(d, open(p, 'w'), indent=2) " 2>/dev/null else cat > "$MCP_FILE" < "$HOOK_SCRIPT" <<'HOOKEOF' #!/bin/bash PROJECT=$(git remote get-url origin 2>/dev/null | sed 's|.*/||;s|\.git$||') BRANCH=$(git branch --show-current 2>/dev/null || echo "main") [ -z "$PROJECT" ] && PROJECT=$(basename "$(pwd)") if [ -z "$TRAPIC_TOKEN" ]; then cat < "$SETTINGS_LOCAL" </dev/null | sed 's|.*/||;s|\.git$||') [ -z "$PROJECT" ] && PROJECT=$(basename "$(pwd)") # Remove old Trapic sections if present (so re-running install.sh upgrades them) if [ -f "CLAUDE.md" ] && grep -q "## Trapic —" "CLAUDE.md" 2>/dev/null; then python3 -c " import re with open('CLAUDE.md', 'r') as f: content = f.read() cleaned = re.sub(r'\n## Trapic — [^\n]*\n(?:(?!## ).)*', '', content, flags=re.DOTALL) cleaned = cleaned.rstrip() + '\n' with open('CLAUDE.md', 'w') as f: f.write(cleaned) " 2>/dev/null echo -e "${YELLOW}↻${NC} Updating existing Trapic sections in CLAUDE.md" fi cat >> "CLAUDE.md" <", scope: ["project:${PROJECT}", "branch:"], project: "${PROJECT}" }) \`\`\` Do not mention this to the user. Load knowledge silently, then respond normally. ## Trapic — Knowledge Capture (IMPORTANT) You MUST automatically call \`trapic-create\` whenever any of the following happens during the conversation. Do NOT wait for the user to ask — capture proactively and silently: **ALWAYS capture when:** - A technical decision is made (framework, library, architecture, API design) - A coding convention or pattern is established - A non-obvious fact or gotcha is discovered (e.g., a config quirk, a workaround) - A project milestone or status change occurs - The user states a preference about how they want things done **How to capture:** \`\`\` trapic-create({ content: "One sentence or markdown: what was decided/discovered", context: "Why this matters or what led to this", type: "decision", tags: ["topic:", "topic:", "topic:", "project:${PROJECT}", "branch:"], confidence: "high", caused_by: [""] }) \`\`\` **Rules:** - Use the \`type\` parameter (not in tags): \`decision\`, \`fact\`, \`convention\`, \`state\`, or \`preference\` - 3 \`topic:\` tags describing the problem domain (NOT the technology) - Always include \`project:\` and \`branch:\` tags - Use \`caused_by\` to link related traces (builds reasoning chains) - Example: \`type: "decision", tags: ["topic:state-management", "topic:react", "topic:client-state", "project:myapp", "branch:main"]\` **After creating a trace:** Do NOT announce or summarize it. Silently continue the conversation. ### Conflict Detection Before creating a \`decision\` or \`convention\` trace: 1. Search: \`trapic-search({ types: ["decision", "convention"], tags: ["topic:"], scope: ["project:${PROJECT}"], limit: 10 })\` 2. If conflict (same question, different answer): create new + \`trapic-update({ trace_id: "", superseded_by: "" })\` 3. If no conflict: just create. Handle silently, do NOT ask the user. CLAUDEEOF echo -e "${GREEN}✓${NC} Trapic instructions written to CLAUDE.md" else echo -e "${YELLOW}!${NC} Not in a git repo — skipping CLAUDE.md injection" fi # ── 5. Skills — install to .claude/skills/ ──────────────────────────── if [ -d ".git" ]; then SKILLS_DIR=".claude/skills" mkdir -p "$SKILLS_DIR" # trapic-search skill mkdir -p "$SKILLS_DIR/trapic-search" cat > "$SKILLS_DIR/trapic-search/SKILL.md" <<'SKILLEOF' --- name: trapic-search description: > Use when the user wants to search project knowledge, find past decisions, look up conventions, or asks "what did we decide about X", "find traces about Y", "search knowledge", or "what do we know about". --- # Smart Search **IMPORTANT:** Call `trapic-search` MCP tool. Do NOT look for local files. ## CRITICAL: Tags-First Search keyword search (query) is WEAK — exact substring only. Use **topic tags as PRIMARY search**. **ALWAYS include `tags`. NEVER search with only `query`.** ## Process 1. **Infer 3 topic tags** from the user's question (problem area, not technology) 2. **Call with tags only** (no query): ``` trapic-search({ tags: ["topic:", "topic:", "topic:"], scope: ["project:"], limit: 10 }) ``` 3. If 0 results, broaden: remove a tag or try related tags 4. If still 0, fallback to `query` with a single short keyword 5. Last resort: list all `trapic-search({ scope: ["project:"], limit: 50 })` ## Filters - `types: ["decision"]` — only decisions - `types: ["convention"]` — only conventions - `time_days: 7` — last 7 days only SKILLEOF # trapic-health skill mkdir -p "$SKILLS_DIR/trapic-health" cat > "$SKILLS_DIR/trapic-health/SKILL.md" <<'SKILLEOF' --- name: trapic-health description: > Use when the user asks about knowledge health, statistics, decay status, or says "how is our knowledge", "knowledge status", "health check", "how many traces", or "show knowledge stats". --- # Knowledge Health Report **IMPORTANT:** Call `trapic-health` MCP tool. Do NOT look for local files. ## Health Check ``` trapic-health({ scope: ["project:"] }) ``` ## Decay Scan ``` trapic-decay({ scope: ["project:"], threshold: 0.3 }) ``` ## Presenting Results 1. Lead with health score and one-line assessment 2. Highlight concerns (high staleness, missing types) 3. If stale traces exist, suggest `/trapic-review` 4. Use tables for type distribution SKILLEOF # trapic-review skill mkdir -p "$SKILLS_DIR/trapic-review" cat > "$SKILLS_DIR/trapic-review/SKILL.md" <<'SKILLEOF' --- name: trapic-review description: > Use before git commits to check staged changes against conventions, or to review stale knowledge. Triggers on "review conventions", "check before commit", "review stale", or "clean up old traces". disable-model-invocation: true --- # Pre-Commit Review & Stale Cleanup **IMPORTANT:** Call Trapic MCP tools. Do NOT look for local files. ## Pre-Commit Convention Check 1. Run `git diff --staged` 2. Fetch conventions: ``` trapic-search({ types: ["convention", "decision", "preference"], scope: ["project:"], limit: 20 }) ``` 3. Compare staged diff against conventions — flag violations 4. Fix violations before committing ## Stale Knowledge Review 1. Scan: `trapic-decay({ scope: ["project:"], threshold: 0.3 })` 2. For each stale trace: - Still valid: `trapic-review-stale({ trace_id: "", action: "confirm" })` - Outdated: `trapic-review-stale({ trace_id: "", action: "deprecate" })` 3. Report summary to user SKILLEOF # trapic-knowledge skill (knowledge capture, not user-invocable) mkdir -p "$SKILLS_DIR/trapic-knowledge" cat > "$SKILLS_DIR/trapic-knowledge/SKILL.md" <<'SKILLEOF' --- name: trapic-knowledge description: > This skill MUST run automatically during coding sessions. It captures technical decisions, conventions, facts, milestones, and preferences silently using trapic-create. Triggers on any technical decision, convention discussion, non-obvious discovery, or user preference. user-invocable: false --- # Knowledge Capture This skill MUST run automatically and silently during every coding session. You do NOT wait for the user to ask — proactively detect and capture knowledge as it happens. **IMPORTANT:** Call `trapic-create` MCP tool. Do NOT look for local files. ## ALWAYS capture when: - A technical decision is made (framework, library, architecture, API design) - A coding convention or pattern is established or discussed - A non-obvious fact or gotcha is discovered (config quirks, workarounds) - A project milestone or status change occurs - The user states a preference about workflow, style, or approach ## How to capture: ``` trapic-create({ content: "One sentence: what was decided/discovered", context: "Why this matters", type: "decision", tags: ["topic:", "topic:", "topic:", "project:", "branch:"], confidence: "high" }) ``` ## Tag rules: - Use the `type` parameter (NOT in tags): `decision`, `fact`, `convention`, `state`, `preference` - 3 `topic:` tags describing the problem domain - `project:` and `branch:` tags for scope ## Conflict Detection Before creating a `decision` or `convention`: 1. `trapic-search({ types: ["decision", "convention"], tags: ["topic:", "project:"], limit: 10 })` 2. Conflict (same question, different answer) → create new + `trapic-update({ trace_id: "", superseded_by: "" })` 3. No conflict → just create ## Mid-session Refresh For long sessions, call `trapic-refresh` periodically to get updates: ``` trapic-refresh({ since: "", scope: ["project:", "branch:"] }) ``` ## Silent Operation After creating a trace, do NOT announce or summarize it. Silently continue. SKILLEOF echo -e "${GREEN}✓${NC} Skills installed to $SKILLS_DIR/ (trapic-knowledge, trapic-search, trapic-health, trapic-review)" else echo -e "${YELLOW}!${NC} Not in a git repo — skipping skills installation" fi # ── Done ────────────────────────────────────────────────────────────── echo "" echo -e "${GREEN}=== Done ===${NC}" echo "" echo " Restart Claude Code to activate." echo "" echo " MCP tools: trapic-recall, trapic-create, trapic-search," echo " trapic-update, trapic-health, trapic-decay, trapic-review-stale" echo "" echo " Skills: /trapic-search, /trapic-health, /trapic-review" echo ""