Files
clawtap/docs/superpowers/plans/2026-03-26-cli-multi-adapter.md
T
kuannnn 42861ea7fa feat: ClawTap v0.1.0 — initial release
Multi-adapter mobile UI for AI coding assistants.
Supports Claude Code, Codex CLI, and Gemini CLI through one interface.

Features:
- Real-time bidirectional sync via tmux + WebSocket
- Cross-AI review (send one AI's output to another for review)
- Multi-review tabs with minimize/expand
- Push notifications (PWA) with smart session-aware filtering
- Three-channel event system (hooks, file watcher, pane monitor)
- Voice input, image paste, draft persistence
- Terminal-native design (JetBrains Mono, dark theme, pixel art claw)
- CLI with --adapter flag on every command
- Zero-overhead fire-and-forget hooks
2026-03-26 10:40:26 +08:00

689 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLI Multi-Adapter Support Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make every `codetap` CLI command support `--adapter` filtering and fix all outdated descriptions that only reference Claude.
**Architecture:** Move `--adapter` flag parsing to the top of the script (before any command handlers), so all commands can access `$ADAPTER`. Update `-a`/`-A` to filter by adapter, `--continue` to filter by adapter, and `hooks` to target specific adapters. Fix all help text and comments.
**Tech Stack:** Bash, Node.js (hooks-cli.mjs)
---
## Complete Issue List
| # | Issue | Type |
|---|---|---|
| 1 | `--adapter` parsed AFTER `-a`/`-A` exits — impossible to combine | Bug |
| 2 | `-a`/`-A` can't filter by adapter | Feature gap |
| 3 | `-a`/`-A` adapter detection uses `pane_current_command` → shows `node` not the adapter name | Bug |
| 4 | `--continue` doesn't pass adapter to resume API | Feature gap |
| 5 | `--continue` doesn't filter by adapter (always picks most recent) | Feature gap |
| 6 | `hooks install/uninstall` can't target specific adapter | Feature gap |
| 7 | Help text says "(Claude or Codex)" — missing Gemini | Text |
| 8 | Header comment says "runs Claude/Codex" — missing Gemini | Text |
| 9 | Header comment missing `--adapter` in usage examples | Text |
| 10 | No-args output doesn't mention `--adapter` | Text |
| 11 | Comment "Claude stores sessions by project dir" is outdated | Text |
| 12 | `<any args>` description unclear | Text |
## File Map
| File | Action | Responsibility |
|---|---|---|
| `bin/codetap` | Modify | Move `--adapter` parsing to top, update all commands, fix all text |
| `bin/hooks-cli.mjs` | Modify | Accept optional adapter name argument |
---
### Task 1: Move `--adapter` Parsing Before All Command Handlers
**Files:**
- Modify: `bin/codetap:24-370` (restructure flag parsing order)
The core structural fix: `--adapter` must be parsed BEFORE any command handler (including `-a`, `-A`, `--version`, `--help`), so all commands can access `$ADAPTER`.
- [ ] **Step 1: Move adapter parsing to right after variable declarations (before the `case` block)**
Move the `--adapter` parsing block (currently at lines 336-370) to immediately after line 22 (`PID_FILE=...`), before the `case "$1" in` block at line 25.
The block to move:
```bash
# --- Parse --adapter flag (before any command handlers) ---
set_adapter() {
case "$1" in
claude) ADAPTER="claude"; ADAPTER_CMD="claude"; YOLO="--dangerously-skip-permissions" ;;
codex) ADAPTER="codex"; ADAPTER_CMD="codex"; YOLO="--dangerously-bypass-approvals-and-sandbox" ;;
gemini) ADAPTER="gemini"; ADAPTER_CMD="gemini"; YOLO="--approval-mode yolo" ;;
esac
}
ADAPTER="claude"
ADAPTER_CMD="claude"
ADAPTER_EXPLICIT=false
prev_arg=""
for arg in "$@"; do
if [ "$prev_arg" = "--adapter" ]; then
ADAPTER_EXPLICIT=true
case "$arg" in
claude) set_adapter claude ;;
codex) set_adapter codex ;;
gemini) set_adapter gemini ;;
*) echo "Unknown adapter: $arg"; exit 1 ;;
esac
fi
prev_arg="$arg"
done
# Strip --adapter and its value from positional args
CLEANED_ARGS=()
skip_next=false
for arg in "$@"; do
if $skip_next; then skip_next=false; continue; fi
if [ "$arg" = "--adapter" ]; then skip_next=true; continue; fi
CLEANED_ARGS+=("$arg")
done
set -- "${CLEANED_ARGS[@]}"
```
Delete the old copy of this block from its current location (lines 336-370).
- [ ] **Step 2: Verify `--version` and `--help` still work**
Run:
```bash
codetap --version
codetap --help
codetap --adapter gemini --version
```
Expected: version prints, help prints, `--adapter gemini --version` still prints version (adapter is parsed but irrelevant for --version).
- [ ] **Step 3: Commit**
```bash
git add bin/codetap
git commit -m "refactor(cli): move --adapter parsing before all command handlers"
```
---
### Task 2: `-a`/`-A` Support `--adapter` Filter + Fix Adapter Detection
**Files:**
- Modify: `bin/codetap` (the `-a`/`-A` handler, lines 249-334)
- [ ] **Step 1: Add adapter filter to the session list**
The current adapter detection (lines 302-307) uses `pane_current_command` which shows `node` for all adapters — broken for detection. Fix by querying the server's `/api/active-sessions` API which has accurate adapter info.
**Note:** The `-a`/`-A` handler is already positioned AFTER `ensure_server()` and `get_auth_token()` (line 249 is after line 200/203). After Task 1 moves `--adapter` parsing to the top, `$ADAPTER` and `$ADAPTER_EXPLICIT` will be available here. No handler relocation needed.
**API note:** `/api/active-sessions` supports `?adapter=` query param but NOT `?cwd=`. For project-level filtering (`-a`), fetch all sessions and filter by `cwd` field client-side in Python.
Replace the entire `-a`/`-A` handler (lines 249-334) with:
```bash
# --- List active sessions ---
if [ "$1" = "--attach" ] || [ "$1" = "-a" ] || [ "$1" = "-A" ]; then
ALL_MODE=false
[ "$1" = "-A" ] && ALL_MODE=true
# Get sessions from the server API (has accurate adapter info)
AUTH_TOKEN=$(get_auth_token)
if [ -n "$AUTH_TOKEN" ]; then
SESSIONS_JSON=$(curl -s $CURL_OPTS "$PROTOCOL://localhost:$PORT/api/active-sessions" \
-H "Authorization: Bearer $AUTH_TOKEN" 2>/dev/null)
else
SESSIONS_JSON="[]"
fi
# Filter by adapter and/or cwd client-side
SESSIONS_JSON=$(echo "$SESSIONS_JSON" | python3 -c "
import sys, json
sessions = json.load(sys.stdin)
adapter_filter = '$ADAPTER' if '$ADAPTER_EXPLICIT' == 'true' else None
cwd_filter = '$(pwd)' if '$ALL_MODE' == 'false' else None
if adapter_filter:
sessions = [s for s in sessions if s.get('adapter') == adapter_filter]
if cwd_filter:
sessions = [s for s in sessions if s.get('cwd') == cwd_filter]
json.dump(sessions, sys.stdout)
" 2>/dev/null)
# Parse and display
COUNT=$(echo "$SESSIONS_JSON" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null)
if [ "$COUNT" = "0" ] || [ -z "$COUNT" ]; then
if [ "$ADAPTER_EXPLICIT" = true ]; then
echo "No active $ADAPTER sessions."
elif [ "$ALL_MODE" = true ]; then
echo "No active sessions."
else
echo "No active sessions for project '$(basename "$(pwd)")'."
echo "Run 'codetap -A' to see all projects, or 'codetap new' to start a new session."
fi
exit 0
fi
if [ "$ALL_MODE" = true ]; then
HEADER="Active sessions (all projects)"
else
HEADER="Active sessions for $(basename "$(pwd)")"
fi
[ "$ADAPTER_EXPLICIT" = true ] && HEADER="$HEADER$ADAPTER only"
echo "$HEADER:"
echo ""
# Render each session
echo "$SESSIONS_JSON" | python3 -c "
import sys, json
sessions = json.load(sys.stdin)
colors = {'claude': '\033[33m', 'codex': '\033[32m', 'gemini': '\033[34m'}
reset = '\033[0m'
home = '$HOME'
for i, s in enumerate(sessions, 1):
adapter = s.get('adapter', '?')
sid = s.get('sessionId', '?')
cwd = s.get('cwd', '')
first = s.get('firstPrompt', '')
color = colors.get(adapter, '\033[90m')
label = f'{color}[{adapter.capitalize()}]{reset}'
print(f' {i}) {label} {sid}')
if $ALL_MODE and cwd:
print(f' Dir: {cwd.replace(home, \"~\")}')
if first:
print(f' {first[:60]}')
print()
" 2>/dev/null
# Interactive selection
read -p "Select (1-$COUNT), or Enter to cancel: " CHOICE
if [ -n "$CHOICE" ]; then
TARGET=$(echo "$SESSIONS_JSON" | python3 -c "
import sys, json
sessions = json.load(sys.stdin)
idx = int('$CHOICE') - 1
if 0 <= idx < len(sessions):
print(sessions[idx]['sessionId'])
" 2>/dev/null)
if [ -n "$TARGET" ]; then
tmux select-window -t "$TMUX_SESSION:$TARGET" 2>/dev/null
tmux attach -t "$TMUX_SESSION"
fi
else
echo "Cancelled."
fi
exit 0
fi
```
- [ ] **Step 2: Verify**
Run:
```bash
# Start a Gemini and Claude session first, then:
codetap -a # Current project sessions
codetap -A # All sessions
codetap -a --adapter gemini # Only Gemini sessions for current project
codetap -A --adapter codex # Only Codex sessions across all projects
```
Expected: Sessions show correct adapter labels from server (not from pane_current_command). Filter works.
- [ ] **Step 3: Commit**
```bash
git add bin/codetap
git commit -m "feat(cli): -a/-A uses server API for accurate adapter info + --adapter filter"
```
---
### Task 3: `--continue` Support `--adapter` Filter
**Files:**
- Modify: `bin/codetap` (`--continue` handler)
- [ ] **Step 1: Update `--continue` to pass adapter to API and filter by adapter**
Replace the `--continue` handler with:
```bash
elif [ "$1" = "--continue" ]; then
shift
# If adapter specified, find most recent session for that adapter
if [ "$ADAPTER_EXPLICIT" = true ]; then
AUTH_TOKEN=$(get_auth_token)
SESSIONS_JSON=$(curl -s $CURL_OPTS "$PROTOCOL://localhost:$PORT/api/active-sessions" \
-H "Authorization: Bearer $AUTH_TOKEN" 2>/dev/null)
LATEST=$(echo "$SESSIONS_JSON" | python3 -c "
import sys, json
sessions = json.load(sys.stdin)
filtered = [s for s in sessions if s.get('adapter') == '$ADAPTER']
if filtered:
# Sort by lastActivity descending
filtered.sort(key=lambda s: s.get('lastActivity', 0), reverse=True)
print(filtered[0]['sessionId'])
" 2>/dev/null)
else
# No adapter specified — find most recent tmux window
LATEST=$(tmux list-windows -t "$TMUX_SESSION" -F '#{window_activity} #{window_name}' 2>/dev/null | grep -v " main$" | sort -rn | head -1 | awk '{print $2}')
fi
if [ -n "$LATEST" ]; then
# Check if the process in the pane is still running
PANE_CMD=$(tmux display -t "$TMUX_SESSION:$LATEST" -p '#{pane_current_command}' 2>/dev/null)
if [ "$PANE_CMD" = "zsh" ] || [ "$PANE_CMD" = "bash" ]; then
# CLI process exited, shell is showing — resume via API
AUTH_TOKEN="${AUTH_TOKEN:-$(get_auth_token)}"
BODY=$(printf '%s\n%s\n%s' "$LATEST" "$ADAPTER" "$(pwd)" | python3 -c 'import sys,json; s,a,c=sys.stdin.read().strip().split("\n"); print(json.dumps({"sessionId":s,"adapter":a,"cwd":c}))' 2>/dev/null)
curl -s $CURL_OPTS -X POST "${PROTOCOL}://localhost:${PORT}/api/sessions/resume" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d "$BODY" >/dev/null 2>&1
fi
tmux select-window -t "$TMUX_SESSION:$LATEST"
else
if [ "$ADAPTER_EXPLICIT" = true ]; then
echo "No active $ADAPTER sessions to continue"
else
echo "No active sessions to continue"
fi
exit 1
fi
tmux attach -t "$TMUX_SESSION"
exit 0
```
- [ ] **Step 2: Verify**
Run:
```bash
codetap --continue # Resume most recent (any adapter)
codetap --continue --adapter gemini # Resume most recent Gemini
codetap --continue --adapter codex # Resume most recent Codex
```
- [ ] **Step 3: Commit**
```bash
git add bin/codetap
git commit -m "feat(cli): --continue supports --adapter filter + passes adapter to resume API"
```
---
### Task 4: `hooks install/uninstall` Support `--adapter` Filter
**Files:**
- Modify: `bin/codetap:84-86` (hooks handler)
- Modify: `bin/hooks-cli.mjs` (accept adapter argument)
- [ ] **Step 1: Update hooks-cli.mjs to accept optional adapter**
Replace `bin/hooks-cli.mjs`:
```javascript
#!/usr/bin/env node
// Standalone hook management — no server needed.
// Usage: node hooks-cli.mjs install|uninstall [adapter]
// adapter: claude, codex, gemini, or omit for all
import { ClaudeHookConfig } from '../server/adapters/claude/hook-config.js';
import { CodexHookConfig } from '../server/adapters/codex/hook-config.js';
import { GeminiHookConfig } from '../server/adapters/gemini/hook-config.js';
const cmd = process.argv[2];
const adapterArg = process.argv[3]; // optional: claude, codex, gemini
if (!cmd || !['install', 'uninstall'].includes(cmd)) {
console.error('Usage: hooks-cli.mjs install|uninstall [claude|codex|gemini]');
process.exit(1);
}
const adapters = {
claude: new ClaudeHookConfig(),
codex: new CodexHookConfig(),
gemini: new GeminiHookConfig(),
};
const targets = adapterArg ? { [adapterArg]: adapters[adapterArg] } : adapters;
if (adapterArg && !adapters[adapterArg]) {
console.error(`Unknown adapter: ${adapterArg}. Use: claude, codex, gemini`);
process.exit(1);
}
for (const [name, config] of Object.entries(targets)) {
if (cmd === 'install') {
config.install();
} else {
config.uninstall();
}
}
```
- [ ] **Step 2: Update bin/codetap hooks handler to pass adapter**
Replace lines 84-86:
```bash
hooks)
if [ "$ADAPTER_EXPLICIT" = true ]; then
node "$SCRIPT_DIR/hooks-cli.mjs" "$2" "$ADAPTER"
else
node "$SCRIPT_DIR/hooks-cli.mjs" "$2"
fi
exit 0 ;;
```
- [ ] **Step 3: Verify**
Run:
```bash
codetap hooks install # Install all
codetap hooks uninstall # Uninstall all
codetap hooks install --adapter gemini # Install Gemini only
codetap hooks uninstall --adapter claude # Uninstall Claude only
```
Wait — `--adapter` is parsed before `hooks` command, but the `hooks` case is in the early `case "$1" in` block which runs before `ensure_server`. Need to check if `--adapter` parsing happens before the case block after Task 1 moves it.
After Task 1, the parsing order is:
1. `--adapter` parsed and stripped (lines 23-55 after move)
2. `case "$1" in` — now `$1` is `hooks` (not `--adapter`)
So `codetap --adapter gemini hooks install` works. But `codetap hooks install --adapter gemini` needs the adapter parsing to handle args in any order. After Task 1's `set --` cleanup, `$1` would be `hooks` and `$2` would be `install` — correct.
But what about `codetap hooks --adapter gemini install`? The `--adapter` stripping would remove `--adapter gemini`, leaving `hooks install`. That works too.
- [ ] **Step 4: Commit**
```bash
git add bin/codetap bin/hooks-cli.mjs
git commit -m "feat(cli): hooks install/uninstall supports --adapter for single-adapter targeting"
```
---
### Task 5: Fix All Help Text and Comments
**Files:**
- Modify: `bin/codetap` (header comments, help text, no-args output)
- [ ] **Step 1: Fix header comment (lines 1-14)**
Replace with:
```bash
#!/bin/bash
# codetap — CLI wrapper that runs AI coding assistants in tmux for mobile sync
#
# Usage:
# codetap # Start server, show URLs
# codetap new # New session (default: claude)
# codetap new --adapter gemini # New Gemini session
# codetap --resume <session-id> # Resume a specific session
# codetap --continue # Resume the most recent session
# codetap --continue --adapter codex # Resume most recent Codex session
# codetap -a # List active sessions (current project)
# codetap -a --adapter gemini # List Gemini sessions only
# codetap -A # List ALL active sessions (all projects)
# codetap stop # Stop the server (graceful cleanup)
# codetap hooks install # Install hooks for all adapters
# codetap hooks install --adapter gemini # Install hooks for Gemini only
# codetap cert # Generate self-signed HTTPS cert
#
# Adapters: claude (default), codex, gemini
# Sessions run inside tmux session "codetap".
# Mobile app auto-connects for real-time sync.
```
- [ ] **Step 2: Fix help text (lines 30-49)**
Replace with:
```bash
cat << 'HELP'
Usage: codetap [options] [command]
Commands:
new Start a new session (default: Claude)
stop Stop the server (graceful cleanup)
hooks install Install hooks (all adapters, or use --adapter)
hooks uninstall Remove hooks (all adapters, or use --adapter)
cert Generate self-signed HTTPS certificate
Options:
-v, --version Show version
-h, --help Show this help
-a List active sessions (current project)
-A List ALL active sessions (all projects)
--adapter <name> Adapter: claude (default), codex, gemini
--resume <session-id> Resume a specific session
--continue Resume most recent session
Examples:
codetap new --adapter gemini Start a Gemini session
codetap -a --adapter codex List active Codex sessions
codetap --continue --adapter gemini Continue most recent Gemini session
codetap hooks install --adapter claude Install Claude hooks only
HELP
```
- [ ] **Step 3: Fix no-args output (lines 218-229)**
Replace with:
```bash
echo ""
echo "CodeTap server is running on port $PORT"
echo ""
echo " Open on your phone:"
if [ -n "$TS_HOST" ]; then echo " https://${TS_HOST} (Tailscale)"; fi
if [ -n "$LAN_IP" ]; then echo " ${PROTOCOL}://${LAN_IP}:${PORT} (LAN)"; fi
echo " http://localhost:${PORT} (this machine)"
echo ""
echo " New session: codetap new [--adapter codex|gemini]"
echo " Continue: codetap --continue"
echo " List sessions: codetap -a"
echo " Stop server: codetap stop"
echo ""
```
- [ ] **Step 4: Remove outdated comment on line 18**
Delete line 18: `# Claude stores sessions by project dir; Codex uses date-based dirs (handled in get_project_sessions)`
- [ ] **Step 5: Verify all text output**
Run:
```bash
codetap --help
codetap --version
codetap 2>&1 | head -15
```
Verify no mention of "Claude" in contexts where it should say "adapter", and Gemini is listed everywhere.
- [ ] **Step 6: Commit**
```bash
git add bin/codetap
git commit -m "fix(cli): update all help text, comments, and output for multi-adapter support"
```
---
### Task 6: E2E Verification — All Commands × All Flags
**Files:** None (testing only)
- [ ] **Step 1: Restart server fresh**
```bash
lsof -ti :3456 | xargs kill -9 2>/dev/null
tmux kill-session -t codetap 2>/dev/null
sleep 2
export CLAUDE_UI_PASSWORD=test
CLAUDE_UI_PASSWORD=test npx tsx server/index.ts > /tmp/codetap-cli-test.log 2>&1 &
sleep 6
curl -sk https://localhost:3456/health
```
- [ ] **Step 2: Test `--version` and `--help`**
```bash
codetap --version
# Expected: codetap v1.3.2
codetap --help
# Expected: Multi-adapter help text with Examples section
# Verify: "claude (default), codex, gemini" appears
# Verify: No "Claude-only" language
```
- [ ] **Step 3: Test `codetap` (no args)**
```bash
codetap
# Expected: Server URLs + multi-adapter usage hints
# Verify: "codetap new [--adapter codex|gemini]" in output
```
- [ ] **Step 4: Test `codetap new` (all 3 adapters)**
```bash
# Create sessions using the API (non-interactive, can't attach tmux from subagent)
TOKEN=$(curl -sk -X POST "https://localhost:3456/api/auth/login" \
-H 'Content-Type: application/json' \
-d '{"password":"test"}' | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
# Claude
RESULT=$(curl -sk -X POST "https://localhost:3456/api/sessions/start" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"adapter\":\"claude\",\"cwd\":\"$(pwd)\"}")
echo "Claude: $RESULT"
# Codex
RESULT=$(curl -sk -X POST "https://localhost:3456/api/sessions/start" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"adapter\":\"codex\",\"cwd\":\"$(pwd)\"}")
echo "Codex: $RESULT"
# Gemini
RESULT=$(curl -sk -X POST "https://localhost:3456/api/sessions/start" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"adapter\":\"gemini\",\"cwd\":\"$(pwd)\"}")
echo "Gemini: $RESULT"
# Verify tmux windows
tmux list-windows -t codetap -F '#{window_name}' | grep -v main
```
Expected: 3 session IDs returned, 3 tmux windows created.
- [ ] **Step 5: Test `-a` and `-A` with and without `--adapter`**
```bash
echo "" | codetap -a 2>&1
# Expected: Lists sessions for current project with [Claude], [Codex], [Gemini] labels
echo "" | codetap -A 2>&1
# Expected: Lists ALL sessions with Dir: info
echo "" | codetap -a --adapter gemini 2>&1
# Expected: Only Gemini sessions listed
echo "" | codetap -A --adapter codex 2>&1
# Expected: Only Codex sessions across all projects
echo "" | codetap -a --adapter claude 2>&1
# Expected: Only Claude sessions for current project
```
- [ ] **Step 6: Test `--continue` with and without `--adapter`**
Can't test tmux attach non-interactively, but verify the session selection logic:
```bash
# Check which session --continue would pick
LATEST=$(tmux list-windows -t codetap -F '#{window_activity} #{window_name}' | grep -v " main$" | sort -rn | head -1 | awk '{print $2}')
echo "Most recent (any adapter): $LATEST"
# With --adapter, verify API query works
TOKEN=$(curl -sk -X POST "https://localhost:3456/api/auth/login" \
-H 'Content-Type: application/json' \
-d '{"password":"test"}' | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
curl -sk "https://localhost:3456/api/active-sessions" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys, json
for s in json.load(sys.stdin):
print(f'{s[\"adapter\"]:8s} {s[\"sessionId\"][:12]}... last={s.get(\"lastActivity\",0)}')
"
```
- [ ] **Step 7: Test `hooks install/uninstall` with and without `--adapter`**
```bash
codetap hooks uninstall
# Expected: All 3 adapters' hooks removed
codetap hooks install --adapter gemini
# Expected: Only Gemini hooks installed
cat ~/.gemini/settings.json | python3 -c "import sys,json; print('hooks' in json.load(sys.stdin))"
# Expected: True
cat ~/.claude/settings.json | python3 -c "import sys,json; d=json.load(sys.stdin); print('hooks' in d and any('codetap' in str(v).lower() for v in d.get('hooks',{}).values()))"
# Expected: False (Claude hooks not installed)
codetap hooks install
# Expected: All 3 adapters' hooks installed
codetap hooks uninstall --adapter claude
# Expected: Only Claude hooks removed
```
- [ ] **Step 8: Test `--resume`**
```bash
# Get a session ID from the list
SID=$(tmux list-windows -t codetap -F '#{window_name}' | grep -v main | head -1)
echo "Resuming: $SID"
# Can't test tmux attach, but verify API:
TOKEN=$(curl -sk -X POST "https://localhost:3456/api/auth/login" \
-H 'Content-Type: application/json' \
-d '{"password":"test"}' | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
curl -sk -X POST "https://localhost:3456/api/sessions/resume" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"sessionId\":\"$SID\",\"adapter\":\"claude\",\"cwd\":\"$(pwd)\"}"
# Expected: {"sessionId":"..."}
```
- [ ] **Step 9: Test `stop` and `cert`**
```bash
codetap stop
# Expected: "Stopping CodeTap server..." → "Server stopped."
# Cert already exists, just verify the command runs
echo "n" | codetap cert
# Expected: "Certificate already exists..." prompt, then exits
```
- [ ] **Step 10: Commit test results as verification log**
```bash
git add -f docs/superpowers/plans/
git commit -m "docs: CLI multi-adapter verification complete"
```