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 <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-29 07:58:45 -04:00
parent 2b381fb777
commit 845f437cb5
7 changed files with 325 additions and 1 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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)"
}
}
}

View file

@ -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)"
}
}
}

184
mcp/install-mcp.sh Executable file
View file

@ -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)."

View file

@ -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",

70
run Executable file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env bash
# prospector — Task Runner
# Usage: ./run <command> [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 <command> [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