#!/bin/bash # Report, from the host, how far the Cowork probe has actually got. # # The probe asks four questions that only the operator can answer in a Cowork # session. But the host-MCP one leaves hard traces on this Mac, and a trace is # a measurement where "what did you see in the UI" is a recollection. This # script reads those traces so each step is verified before the next begins. # # Two earlier queries in this script were WRONG and the measurement caught # both; the corrections are written down here so they are not re-derived: # # 1. It searched for a directory named after the plugin. Cowork installs into # rpm/plugin_/, so the name never appears in a path. The # manifest at rpm/manifest.json is the index; read that instead. # 2. It expected ~/Library/Logs/Claude/mcp-server-.log. That naming is # for Claude Desktop's own connectors. A plugin's MCP server is logged by # LocalMcpServerManager into main.log instead. No file at the guessed path # meant the guess was wrong, not that nothing had happened. # # It reports; it does not gate. Exit 0 when every check has landed, 1 while any # is pending. bash 3.2-clean, ASCII-only, read-only. set -u SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) PROBE_DOC="$REPO_ROOT/docs/cowork-probe.md" FIXTURE="$REPO_ROOT/tests/fixtures/cowork-probe" ARCHIVE="${JOBBSOK_PROBE_ARCHIVE:-/tmp/jobbsok-probe.plugin}" MAIN_LOG="$HOME/Library/Logs/Claude/main.log" SESSIONS="$HOME/Library/Application Support/Claude/local-agent-mode-sessions" PLUGIN_NAME="jobbsok-probe" SERVER_KEY="plugin:jobbsok-probe:probe-tools" pending=0 done_() { printf ' [DONE] %s\n' "$1"; } pend() { printf ' [PENDING] %s\n' "$1"; pending=$((pending+1)); } info() { printf ' %s\n' "$1"; } printf '\n=== 1. Probe vehicle and archive ============================================\n' n=$(find "$FIXTURE" -type f 2>/dev/null | wc -l | tr -d ' ') if [ "$n" = "4" ]; then done_ "probe vehicle: 4 files under tests/fixtures/cowork-probe" else pend "probe vehicle: expected 4 files, found $n" fi if [ -f "$ARCHIVE" ]; then a=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | wc -l | tr -d ' ') leak=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | grep -cE '^\.git|STATE\.md|\.venv|^\.claude/') if [ "$a" = "4" ] && [ "$leak" = "0" ]; then done_ "archive: $ARCHIVE ($a files, no leaked entries)" else pend "archive: $ARCHIVE has $a entries and $leak leaked ones - rebuild it" fi else pend "archive not built: $ARCHIVE" fi printf '\n=== 2. Is jobbsok-probe installed in Cowork? =================================\n' # python3 does the walking: the path contains "Application Support", and an # unquoted $(find ...) in a for-loop splits on that space. That exact bug is # what made this check report "not installed" while the plugin was running. INSTALL_DIR=$(python3 - "$SESSIONS" "$PLUGIN_NAME" <<'PYEOF' import json, os, sys sessions, name = sys.argv[1], sys.argv[2] for dirpath, dirnames, filenames in os.walk(sessions): if os.path.basename(dirpath) != "rpm" or "manifest.json" not in filenames: continue try: data = json.load(open(os.path.join(dirpath, "manifest.json"))) except Exception: continue for p in data.get("plugins", []): if p.get("name") == name: print("%s\t%s\t%s\t%s" % ( os.path.join(dirpath, p["id"]), p["id"], p.get("marketplaceName", "?"), p.get("updatedAt", "?"))) sys.exit(0) PYEOF ) if [ -n "$INSTALL_DIR" ]; then D=$(printf '%s' "$INSTALL_DIR" | cut -f1) ID=$(printf '%s' "$INSTALL_DIR" | cut -f2) MP=$(printf '%s' "$INSTALL_DIR" | cut -f3) AT=$(printf '%s' "$INSTALL_DIR" | cut -f4) done_ "listed in rpm/manifest.json as $ID (marketplace: $MP, updated $AT)" f=$(find "$D" -type f 2>/dev/null | wc -l | tr -d ' ') if [ "$f" = "4" ]; then done_ "installed copy holds 4 files, matching the archive" else pend "installed copy holds $f files, expected 4" fi v=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['version'])" "$D/.claude-plugin/plugin.json" 2>/dev/null) if [ "$v" = "0.0.1" ]; then done_ "installed manifest version is 0.0.1 - not a stale cached build" else pend "installed manifest version is '$v', expected 0.0.1 - Cowork served a cached build" fi else pend "no plugin named $PLUGIN_NAME in any rpm/manifest.json" fi printf '\n=== 3. Did Cowork spawn probe-tools on THIS Mac? =============================\n' printf ' (the -Host-MCP: question, measured rather than recalled)\n' if [ -f "$MAIN_LOG" ]; then conn=$(grep -a "Connected to $SERVER_KEY" "$MAIN_LOG" 2>/dev/null | tail -1) neg=$(grep -a "$SERVER_KEY negotiated protocol version" "$MAIN_LOG" 2>/dev/null | tail -1) if [ -n "$conn" ]; then done_ "connected: $(printf '%s' "$conn" | sed 's/.*\[LocalMcpServerManager\] //')" else pend "main.log has no 'Connected to $SERVER_KEY' line" fi if [ -n "$neg" ]; then done_ "handshake: $(printf '%s' "$neg" | sed 's/.*negotiated/negotiated/')" else pend "no protocol negotiation recorded" fi call=$(grep -a 'probe_ping' "$MAIN_LOG" 2>/dev/null | tail -1) if [ -n "$call" ]; then done_ "probe_ping seen in the log" else info "no probe_ping line in main.log yet - the connection lifecycle is" info "logged there but an individual tool call may not be. Confirm the" info "call by its answer in the Cowork chat, not by this line's absence." fi else pend "no main.log at $MAIN_LOG" fi proc=$(pgrep -fl probe_tools.py 2>/dev/null | head -1) if [ -n "$proc" ]; then done_ "process alive: pid $(printf '%s' "$proc" | awk '{print $1}')" interp=$(printf '%s' "$proc" | grep -oE '/[^ ]*python3?' | head -1) [ -n "$interp" ] && info "interpreter: $interp (host, not sandbox)" else info "no probe_tools.py process at this instant - servers may be started on" info "demand, so absence here is not a nei. The log above is the record." fi printf '\n=== 4. Are the four answers written down? ===================================\n' RE='^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT): +(lokal VM|sky|ja|nei)\b' c=$(grep -cE "$RE" "$PROBE_DOC" 2>/dev/null) if [ "$c" = "4" ]; then done_ "all four answered - plan Step 1 Verify passes" else pend "$c of 4 answered in docs/cowork-probe.md" fi grep -E '^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT):' "$PROBE_DOC" 2>/dev/null | sed 's|^| |' printf '\n============================================================================\n' if [ "$pending" -eq 0 ]; then printf 'ALL CHECKS LANDED.\n' exit 0 fi printf '%s check(s) still pending.\n' "$pending" exit 1