Files
AgentHub/plugins/agenthub-devtools/bin/agenthub-doctor
T
nntrivi2001 17d56d0e01 refactor!: rebrand CCAM to AgentHub
Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).

Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.

Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.

Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
2026-09-07 12:09:15 +07:00

195 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# agenthub-doctor — Diagnostic tool for AgentHub
# Usage: agenthub-doctor [--quick] [--deep] [--fix]
set -euo pipefail
DASHBOARD_URL="${AGENTHUB_DASHBOARD_URL:-http://localhost:4820}"
HOOK_HANDLER_NAME="hook-handler.js"
CLAUDE_SETTINGS="${HOME}/.claude/settings.json"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
NC='\033[0m'
BOLD='\033[1m'
PASS=0
WARN=0
FAIL=0
usage() {
cat <<EOF
agenthub-doctor — AgentHub Diagnostic Tool
USAGE:
agenthub-doctor [OPTIONS]
OPTIONS:
--quick Run basic connectivity checks only
--deep Run all checks including database integrity
--fix Attempt to auto-fix common issues
--json Output results as JSON
--help Show this help
ENVIRONMENT:
AGENTHUB_DASHBOARD_URL Dashboard URL (default: http://localhost:4820)
EOF
exit 0
}
check_pass() { echo -e " ${GREEN}✅ $1${NC}"; ((PASS++)); }
check_warn() { echo -e " ${YELLOW}⚠️ $1${NC}"; ((WARN++)); }
check_fail() { echo -e " ${RED}❌ $1${NC}"; ((FAIL++)); }
header() {
echo ""
echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${CYAN}║ AGENT MONITOR DIAGNOSTIC REPORT ║${NC}"
echo -e "${BOLD}${CYAN}║ $(date '+%Y-%m-%d %H:%M:%S %Z') ║${NC}"
echo -e "${BOLD}${CYAN}╠══════════════════════════════════════════════╣${NC}"
echo ""
}
check_api() {
echo -e "${BOLD}API Server${NC}"
local start_ms end_ms duration_ms
start_ms=$(date +%s%3N 2>/dev/null || python3 -c 'import time; print(int(time.time()*1000))')
if response=$(curl -sf --max-time 5 "${DASHBOARD_URL}/api/health" 2>/dev/null); then
end_ms=$(date +%s%3N 2>/dev/null || python3 -c 'import time; print(int(time.time()*1000))')
duration_ms=$((end_ms - start_ms))
if [ "$duration_ms" -lt 500 ]; then
check_pass "API responding (${duration_ms}ms)"
else
check_warn "API slow (${duration_ms}ms — expected <500ms)"
fi
else
check_fail "API unreachable at ${DASHBOARD_URL}"
echo " → Start the server: npm start (from the project directory)"
return 1
fi
}
check_endpoints() {
echo -e "${BOLD}API Endpoints${NC}"
local endpoints=("sessions?limit=1" "events?limit=1" "analytics" "pricing" "stats" "settings/info")
local pass_count=0
local total=${#endpoints[@]}
for ep in "${endpoints[@]}"; do
if curl -sf --max-time 5 "${DASHBOARD_URL}/api/${ep}" > /dev/null 2>&1; then
((pass_count++))
fi
done
if [ "$pass_count" -eq "$total" ]; then
check_pass "All ${total} endpoints responding"
elif [ "$pass_count" -gt 0 ]; then
check_warn "${pass_count}/${total} endpoints responding"
else
check_fail "No endpoints responding"
fi
}
check_database() {
echo -e "${BOLD}Database${NC}"
local stats
if stats=$(curl -sf --max-time 5 "${DASHBOARD_URL}/api/stats" 2>/dev/null); then
local sessions events
sessions=$(echo "$stats" | jq -r '.total_sessions // 0')
events=$(echo "$stats" | jq -r '.total_events // 0')
check_pass "Database OK (${sessions} sessions, ${events} events)"
else
check_fail "Database query failed"
fi
}
check_hooks() {
echo -e "${BOLD}Hook Configuration${NC}"
if [ ! -f "$CLAUDE_SETTINGS" ]; then
check_fail "Claude settings not found at ${CLAUDE_SETTINGS}"
echo " → Run: npm run install-hooks (from the project directory)"
return
fi
local hook_count
hook_count=$(jq '[.hooks // {} | to_entries[] | .value[] | .hooks[]? ] | length' "$CLAUDE_SETTINGS" 2>/dev/null || echo "0")
if [ "$hook_count" -ge 7 ]; then
check_pass "Hooks configured (${hook_count} hook entries)"
elif [ "$hook_count" -gt 0 ]; then
check_warn "Partial hook setup (${hook_count}/7+ expected)"
echo " → Run: npm run install-hooks"
else
check_fail "No hooks configured"
echo " → Run: npm run install-hooks (from the project directory)"
fi
}
check_data_freshness() {
echo -e "${BOLD}Data Freshness${NC}"
local events
if events=$(curl -sf --max-time 5 "${DASHBOARD_URL}/api/events?limit=1" 2>/dev/null); then
local last_event
last_event=$(echo "$events" | jq -r '.[0].created_at // empty' 2>/dev/null)
if [ -n "$last_event" ]; then
check_pass "Last event: ${last_event}"
else
check_warn "No events found — dashboard may be newly set up"
fi
else
check_warn "Could not check data freshness"
fi
}
summary() {
local total=$((PASS + WARN + FAIL))
echo ""
echo -e "${BOLD}${CYAN}╠══════════════════════════════════════════════╣${NC}"
if [ "$FAIL" -eq 0 ] && [ "$WARN" -eq 0 ]; then
echo -e "${BOLD}${GREEN}║ Overall: HEALTHY (${PASS}/${total} checks passed) ║${NC}"
elif [ "$FAIL" -eq 0 ]; then
echo -e "${BOLD}${YELLOW}║ Overall: DEGRADED (${WARN} warnings) ║${NC}"
else
echo -e "${BOLD}${RED}║ Overall: UNHEALTHY (${FAIL} failures) ║${NC}"
fi
echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════╝${NC}"
echo ""
}
# --- Main ---
MODE="standard"
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h) usage ;;
--quick) MODE="quick"; shift ;;
--deep) MODE="deep"; shift ;;
--fix) MODE="fix"; shift ;;
--json) MODE="json"; shift ;;
*) echo "Unknown option: $1" >&2; usage ;;
esac
done
header
check_api || { summary; exit 1; }
echo ""
if [ "$MODE" != "quick" ]; then
check_endpoints
echo ""
check_database
echo ""
fi
check_hooks
echo ""
check_data_freshness
summary
exit "$FAIL"