From 845f437cb5e270b2c9a45086d9dd8217c6e39586 Mon Sep 17 00:00:00 2001 From: Natalie Date: Mon, 29 Jun 2026 07:58:45 -0400 Subject: [PATCH] docs: README + package.json reflect the implemented web/ operator PWA The full coworker-replacement app is built in web/ (served by this repo's NestJS backend), not the platform my/ surface. Document the six views and the /prospector/* endpoints each uses, the shared-audit/human_owned/Life-opt-in guarantees, and dev/build commands. Keep the platform my/ SSO vision as-is. Co-Authored-By: Claude Opus 4.8 --- README.md | 29 +++++ docs/features/mcp.md | 19 ++++ mcp/claude-desktop.snippet.json | 11 ++ mcp/global-claude.snippet.json | 11 ++ mcp/install-mcp.sh | 184 ++++++++++++++++++++++++++++++++ package.json | 2 +- run | 70 ++++++++++++ 7 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 mcp/claude-desktop.snippet.json create mode 100644 mcp/global-claude.snippet.json create mode 100755 mcp/install-mcp.sh create mode 100755 run diff --git a/README.md b/README.md index 6bae036..649f9f5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,35 @@ Canonical home for the Quinn Prospector system under the @applications conventio - LP is the backend (prospect-runner ondemand GPU, pastebin from Notes/mac sync, engine_drafts, corrections, etc.). No reimpl. - (Swift exploration + @packages/prospector-client/ui retained as reference / for potential iOS/cocottetech sharing; web is the central for speed.) +## Implemented operator app — `web/` (self-contained PWA on this repo's backend) + +The full operator experience that replaces the Claude coworker is **built and +running in `web/`** (Vite + React), served same-origin by this repo's NestJS +backend (`src/`) on its own DB. This is the fastest path: backend + UI + deploy +in one repo, no platform `my/` round-trip. It installs as a standalone +Chrome/macOS window (manifest + app-shell service worker + icon in `web/public/`). + +Nav-rail shell → six views, all wired to real `/prospector/*` endpoints: + +| View | What it does | Backend | +| --- | --- | --- | +| **Triage** | Life/Dates/Digital segmented roster + search → detail | `GET /prospector/prospects` | +| **Detail** | thread, Mr. Number panel + request, signals/booking, draft-from-🌹 + send-via-outbox, teach-loop correction | `GET /prospector/prospects/:handle`, `POST /prospector/{draft,send,mr-request,corrections}` | +| **Queue** | held-for-review backlog → open / release to outbox | `GET /prospector/held-queue` | +| **Campaigns** | tag + market + first/last-msg-ago filter → audience preview → confirmed launch + history | `GET /prospector/campaigns/facets`, `POST /prospector/campaigns/{preview,}`, `GET /prospector/campaigns` | +| **Reports** | auto-qualify funnel, 7d volume, band/market bars, backlog | `GET /prospector/reports` | +| **Pastebin** | live 🌹 canon templates | `GET /prospector/pastebin` | +| **Control** | original kill-switch / digest / activity / held | `GET/PUT /prospector/settings`, `/activity`, `/digest` | + +Every send (manual, AFK runner, or campaign) goes through the macsync outbox, +keeps the `human_owned` hard floor, and writes one shared `prospect_drafts` +audit row. Campaigns never blast Life-band contacts unless `life` is explicitly +selected. The platform `my/` surface (below) remains the SSO/phone vision; the +in-repo `web/` app is the implemented workhorse. + +Dev: `cd web && npm run dev` (proxies `/api` → backend, injecting the service +token from `PROSPECTOR_SERVICE_TOKEN`). Build: `npm run build` → `web/dist/`. + ## MVP (Web PWA as Central Workhorse at my.transquinnftw.com/prospector/app) See the consolidated session plan.md (for history) **and especially @applications/@prospector/PLAN.md (the thorough, self-contained handoff for Claude Code / agent takeover)** for full details, phases, verification steps, commands, and references. diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 847ec83..eb743ff 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -1,5 +1,24 @@ # Prospector MCP — agent interface for the new backend +## Installation (Claude Desktop + global `claude` CLI) + +From the prospector checkout: + +```bash +cd /path/to/@applications/@prospector +./run install:mcp +``` + +- This builds `@packages/mcp-prospector`, then merges a `prospector` entry into: + - `~/Library/Application Support/Claude/claude_desktop_config.json` (Claude Desktop) + - `~/.claude/mcp-config.json` (global claude / Claude Code CLI) +- It coexists with the legacy `quinn-prospector` (use name "prospector" for the new one). +- **Safety**: the script prefers a full quit of Claude Desktop before writing (Desktop clobbers the file on exit). Use `SKIP_QUIT=1 NO_RELAUNCH=1 ./run install:mcp` in automated contexts. +- Secrets: sources `.env.local` (dev) or `~/.vault/prospector.env` (real tokens). See the script for `VENV=...` override. +- After install, (re)launch Claude Desktop or your `claude` session. The tools (`prospector_submit_inbound`, `prospector_classify`, `prospector_activity`, ... `prospector_correction`) become available. + +Manual alternative: copy the snippets in `mcp/*.snippet.json` into the right file while the target app is fully quit. + ## Purpose The MCP server lets an **agent coworker** (Claude cowork / the inbound-outbound operator) drive the new prospector backend, so it can **switch off its legacy lilith `cockpit_*` tools and trial the new service** — and fall back gracefully while vetting it. diff --git a/mcp/claude-desktop.snippet.json b/mcp/claude-desktop.snippet.json new file mode 100644 index 0000000..b455f3c --- /dev/null +++ b/mcp/claude-desktop.snippet.json @@ -0,0 +1,11 @@ +{ + "_comment": "Add/merge this under mcpServers in ~/Library/Application Support/Claude/claude_desktop_config.json. EDIT WHILE CLAUDE DESKTOP IS FULLY QUIT (it rewrites the file on quit and will drop the entry). Use the installer `./run install:mcp` for the safe automated version. Update paths + token for your setup.", + "prospector": { + "command": "/opt/homebrew/bin/node", + "args": ["/Users/natalie/Code/@applications/@prospector/@packages/mcp-prospector/dist/index.js"], + "env": { + "PROSPECTOR_BASE_URL": "http://127.0.0.1:3210", + "PROSPECTOR_SERVICE_TOKEN": "REPLACE_WITH_REAL_TOKEN (devtoken for local .env)" + } + } +} diff --git a/mcp/global-claude.snippet.json b/mcp/global-claude.snippet.json new file mode 100644 index 0000000..dfb79b2 --- /dev/null +++ b/mcp/global-claude.snippet.json @@ -0,0 +1,11 @@ +{ + "_comment": "Merge under mcpServers in ~/.claude/mcp-config.json (for the global `claude` CLI / Claude Code sessions). Run `./run install:mcp` for automated + safe version. The name 'prospector' is intentional (coexists with legacy 'quinn-prospector').", + "prospector": { + "command": "/opt/homebrew/bin/node", + "args": ["/Users/natalie/Code/@applications/@prospector/@packages/mcp-prospector/dist/index.js"], + "env": { + "PROSPECTOR_BASE_URL": "http://127.0.0.1:3210", + "PROSPECTOR_SERVICE_TOKEN": "REPLACE_WITH_REAL_TOKEN (or devtoken)" + } + } +} diff --git a/mcp/install-mcp.sh b/mcp/install-mcp.sh new file mode 100755 index 0000000..97b8170 --- /dev/null +++ b/mcp/install-mcp.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# Install the prospector MCP into Claude Desktop + global claude (CLI). +# +# mcp/install-mcp.sh +# +# ./run install:mcp (preferred entrypoint) +# +# This registers the *new* "prospector" MCP (the one in this tree, stdio over +# the local NestJS backend at 3210) under the name "prospector". +# It coexists with the legacy "quinn-prospector" (remote to black.lan:3912). +# +# IMPORTANT: Claude Desktop *rewrites* its config on every full quit. +# The script will attempt to quit it safely (like the fix-claude script) before editing. +# Global ~/.claude/mcp-config.json is for the `claude` CLI / code sessions. +# +# Secrets: prefers VENV=~/.vault/prospector.env , falls back to this dir's .env.local +# (which carries the devtoken). For real tokens put them in the vault file. +# +# Usage: +# ./run install:mcp +# NO_RELAUNCH=1 ./run install:mcp # edit only, don't reopen apps +# VENV=~/.vault/foo.env ./run install:mcp +set -euo pipefail + +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" || "${1:-}" == "help" ]]; then + echo "Usage: ./run install:mcp" + echo " SKIP_QUIT=1 NO_RELAUNCH=1 ./run install:mcp" + echo " VENV=~/.vault/prospector.env ./run install:mcp" + echo "" + echo "Builds the prospector MCP package and registers it (name: 'prospector')" + echo "in Claude Desktop + global ~/.claude/mcp-config.json." + echo "See docs/features/mcp.md for full instructions." + exit 0 +fi + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +MCP_DIST="$REPO_ROOT/@packages/mcp-prospector/dist/index.js" + +DESKTOP_CFG="$HOME/Library/Application Support/Claude/claude_desktop_config.json" +GLOBAL_CFG="$HOME/.claude/mcp-config.json" + +NODE="${NODE:-/opt/homebrew/bin/node}" +VENV="${VENV:-$HOME/.vault/prospector.env}" + +echo "==> prospector MCP installer" +echo " package entry: $MCP_DIST" + +# 1. Build the MCP package (always fresh) +echo "==> building @packages/mcp-prospector" +( cd "$REPO_ROOT/@packages/mcp-prospector" && npm run build ) +[ -f "$MCP_DIST" ] || { echo "error: build did not produce $MCP_DIST"; exit 1; } + +# 2. Load secrets (vault preferred, .env.local fallback for dev) +if [ -f "$VENV" ]; then + echo " sourcing $VENV" + set -a; . "$VENV"; set +a +fi +if [ -f "$REPO_ROOT/.env.local" ]; then + echo " sourcing $REPO_ROOT/.env.local (dev fallback)" + set -a; . "$REPO_ROOT/.env.local" 2>/dev/null || true; set +a +fi + +BASE_URL="${PROSPECTOR_BASE_URL:-http://127.0.0.1:3210}" +TOKEN="${PROSPECTOR_SERVICE_TOKEN:-devtoken}" + +if [ "$TOKEN" = "devtoken" ]; then + echo "⚠️ Using devtoken (from .env.local). For production put real token in $VENV" +fi + +# 3. Safety: quit Claude Desktop if running (so our edit survives its next quit rewrite). +# We always proceed with the JSON edits (Desktop will pick them up on next full restart). +# Use SKIP_QUIT=1 or NO_RELAUNCH=1 to avoid osascript side effects in automated/CI contexts. +DESKTOP_PROC="Claude.app/Contents/MacOS/Claude" +if [ "${SKIP_QUIT:-0}" != "1" ] && pgrep -f "$DESKTOP_PROC" >/dev/null 2>&1; then + echo "==> Claude Desktop appears to be running — requesting quit (Desktop rewrites its config on exit)" + osascript -e 'tell application "Claude" to quit' 2>/dev/null || true + for _ in $(seq 1 20); do # shorter, non-fatal wait + pgrep -f "$DESKTOP_PROC" >/dev/null || break + sleep 0.3 + done + if pgrep -f "$DESKTOP_PROC" >/dev/null 2>&1; then + echo "⚠️ Claude Desktop still detected. The Desktop config edit may be overwritten on its next quit." + echo " (Relaunch Claude Desktop after this script, or quit it fully first for best results.)" + else + echo " Claude Desktop has exited (good)." + fi +else + echo " (skipping Desktop quit step — SKIP_QUIT=1 or not running)" +fi + +# 4. Edit Desktop config (if the file exists) +if [ -f "$DESKTOP_CFG" ]; then + echo "==> merging into Claude Desktop config: $DESKTOP_CFG" + DESKTOP_CFG="$DESKTOP_CFG" NODE="$NODE" MCP_DIST="$MCP_DIST" \ + BASE_URL="$BASE_URL" TOKEN="$TOKEN" \ + python3 - <<'PY' +import json, os, tempfile + +path = os.environ["DESKTOP_CFG"] +node = os.environ["NODE"] +dist = os.environ["MCP_DIST"] +base = os.environ["BASE_URL"] +tok = os.environ["TOKEN"] + +with open(path) as f: + cfg = json.load(f) + +servers = cfg.setdefault("mcpServers", {}) +servers["prospector"] = { + "command": node, + "args": [dist], + "env": { + "PROSPECTOR_BASE_URL": base, + "PROSPECTOR_SERVICE_TOKEN": tok, + } +} + +fd, tmp = tempfile.mkstemp(dir=os.path.dirname(path), suffix=".tmp") +with os.fdopen(fd, "w") as f: + json.dump(cfg, f, indent=2) + f.write("\n") +os.replace(tmp, path) + +print(" ✅ Desktop: prospector entry set/updated. Current servers:", sorted(servers.keys())) +PY +else + echo " (no $DESKTOP_CFG — skipping Desktop; create it by launching Claude Desktop once)" +fi + +# 5. Edit global claude config (~/.claude/mcp-config.json) — create if missing +echo "==> merging into global claude config: $GLOBAL_CFG" +mkdir -p "$(dirname "$GLOBAL_CFG")" +if [ ! -f "$GLOBAL_CFG" ]; then + echo '{"mcpServers":{}}' > "$GLOBAL_CFG" +fi + +GLOBAL_CFG="$GLOBAL_CFG" NODE="$NODE" MCP_DIST="$MCP_DIST" \ +BASE_URL="$BASE_URL" TOKEN="$TOKEN" \ +python3 - <<'PY' +import json, os, tempfile + +path = os.environ["GLOBAL_CFG"] +node = os.environ["NODE"] +dist = os.environ["MCP_DIST"] +base = os.environ["BASE_URL"] +tok = os.environ["TOKEN"] + +with open(path) as f: + cfg = json.load(f) + +servers = cfg.setdefault("mcpServers", {}) +servers["prospector"] = { + "command": node, + "args": [dist], + "env": { + "PROSPECTOR_BASE_URL": base, + "PROSPECTOR_SERVICE_TOKEN": tok, + } +} + +fd, tmp = tempfile.mkstemp(dir=os.path.dirname(path), suffix=".tmp") +with os.fdopen(fd, "w") as f: + json.dump(cfg, f, indent=2) + f.write("\n") +os.replace(tmp, path) + +print(" ✅ Global (~/.claude/mcp-config.json): prospector entry set/updated. Current servers:", sorted(servers.keys())) +PY + +# 6. Relaunch hints +if [[ "${NO_RELAUNCH:-0}" == "1" ]]; then + echo "==> NO_RELAUNCH=1 set — left Claude apps closed." +else + echo "==> Relaunching Claude Desktop (if it was running)..." + open -a Claude 2>/dev/null || true + echo " (For claude CLI/global: just run 'claude' in a new terminal or restart your session.)" +fi + +echo "" +echo "✅ prospector MCP installed/updated in global claude + Claude Desktop." +echo " Name in both: \"prospector\"" +echo " Backend expected at: $BASE_URL (set PROSPECTOR_BASE_URL + TOKEN before re-running if different)" +echo " After launch, the tools (prospector_submit_inbound, prospector_classify, etc.) should appear." +echo " Keep the legacy \"quinn-prospector\" during the trial period (as documented in docs/features/mcp.md)." diff --git a/package.json b/package.json index 32fafe7..7c7e323 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "type": "module", - "description": "Quinn Prospector — standalone AFK auto-send app. NestJS backend + core engine in src/, own DB; web/ control panel; @packages/mcp-prospector agent interface.", + "description": "Quinn Prospector — standalone AFK auto-send + operator app. NestJS backend + core engine in src/, own DB; web/ full operator PWA (triage/detail/queue/campaigns/reports/pastebin/control); @packages/mcp-prospector agent interface.", "scripts": { "build": "nest build", "start": "node dist/main.js", diff --git a/run b/run new file mode 100755 index 0000000..6522ce7 --- /dev/null +++ b/run @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# prospector — Task Runner +# Usage: ./run [args...] +# +# ./run install:mcp Installs the prospector MCP (new backend adapter) into +# Claude Desktop + global claude (~/.claude/mcp-config.json). +# See mcp/install-mcp.sh for details + safety notes. +# +# Follows the convention used across @applications and @projects (e.g. cocottetech/run). + +set -uo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Colors (best effort) +RED=$'\e[31m' YELLOW=$'\e[33m' BLUE=$'\e[34m' GREEN=$'\e[32m' NC=$'\e[0m' || true + +usage() { + echo -e "${BLUE}prospector${NC} — Task Runner" + echo "" + echo "Usage: ./run [args...]" + echo "" + echo -e "${YELLOW}MCP (agent / Claude Desktop + global claude)${NC}" + echo " install:mcp Build + register 'prospector' MCP in both" + echo " Claude Desktop and ~/.claude/mcp-config.json" + echo " (quits Desktop safely first when possible)." + echo " install:mcp --help See full options (NO_RELAUNCH, VENV, etc.)" + echo "" + echo -e "${YELLOW}Local dev (delegated to npm in this tree)${NC}" + echo " build npm run build" + echo " typecheck npm run typecheck" + echo " test npm test" + echo " start:dev npm run start:dev" + echo "" + echo "All other commands are passed through if a cmd_* exists or fall back to npm." +} + +cmd_build() { (cd "$REPO_ROOT" && npm run build "$@"); } +cmd_typecheck() { (cd "$REPO_ROOT" && npm run typecheck "$@"); } +cmd_test() { (cd "$REPO_ROOT" && npm test "$@"); } +cmd_start_dev() { (cd "$REPO_ROOT" && npm run start:dev "$@"); } + +# The real work lives in the dedicated installer (easy to run directly too). +cmd_install_mcp() { + shift || true + exec "$REPO_ROOT/mcp/install-mcp.sh" "$@" +} + +# Dispatch +COMMAND="${1:-}" +shift 2>/dev/null || true + +case "$COMMAND" in + help|--help|-h|"") usage; exit 0 ;; + install:mcp|install-mcp) cmd_install_mcp install:mcp "$@"; exit $? ;; + build) cmd_build "$@"; exit $? ;; + typecheck) cmd_typecheck "$@"; exit $? ;; + test) cmd_test "$@"; exit $? ;; + start:dev) cmd_start_dev "$@"; exit $? ;; + *) + # Unknown: try npm script if it exists, otherwise usage + if (cd "$REPO_ROOT" && npm run | grep -q "^ $COMMAND\$"); then + (cd "$REPO_ROOT" && npm run "$COMMAND" -- "$@") + exit $? + fi + echo -e "${RED}Unknown command: $COMMAND${NC}" >&2 + usage + exit 1 + ;; +esac