#!/bin/bash
set -euo pipefail

KUGETSU_DIR="${KUGETSU_DIR:-$HOME/.kugetsu}"
SESSIONS_DIR="$KUGETSU_DIR/sessions"
WORKTREES_DIR="$KUGETSU_DIR/worktrees"
REPOS_CONFIG="$KUGETSU_DIR/repos.json"
INDEX_FILE="$KUGETSU_DIR/index.json"
NOTIFICATIONS_FILE="$KUGETSU_DIR/notifications.json"
LOGS_DIR="$KUGETSU_DIR/logs"
ENV_DIR="${ENV_DIR:-$KUGETSU_DIR/env}"
VERBOSITY_DIR="$KUGETSU_DIR/verbosity"

MAX_CONCURRENT_AGENTS="${MAX_CONCURRENT_AGENTS:-3}"
KUGETSU_VERBOSITY="${KUGETSU_VERBOSITY:-default}"
WORKTREE_CHECK_PR_STATUS="${WORKTREE_CHECK_PR_STATUS:-true}"

# Load user config overrides (~/.kugetsu/config)
if [ -f "$KUGETSU_DIR/config" ]; then
    source "$KUGETSU_DIR/config"
fi

mask_sensitive_vars() {
    local line="$1"
    for var in GITEA_TOKEN GITHUB_TOKEN GITLAB_TOKEN API_KEY PASSWORD TOKEN SECRET; do
        if [[ "$line" =~ $var ]]; then
            line=$(echo "$line" | sed -E "s/=.*/=***MASKED***/")
        fi
    done
    echo "$line"
}

load_agent_env() {
    local agent_type="${1:-base}"
    local env_file="$ENV_DIR/${agent_type}.env"
    
    if [ -f "$env_file" ]; then
        set -a
        source "$env_file"
        set +a
    elif [ -f "$ENV_DIR/default.env" ]; then
        set -a
        source "$ENV_DIR/default.env"
        set +a
    fi
}

count_active_dev_sessions() {
    local count=0
    if [ -d "$SESSIONS_DIR" ]; then
        for session_file in "$SESSIONS_DIR"/*.json; do
            if [ -f "$session_file" ]; then
                local filename=$(basename "$session_file")
                if [ "$filename" != "base.json" ]; then
                    count=$((count + 1))
                fi
            fi
        done
    fi
    echo "$count"
}

usage() {
    cat << 'EOF'
kugetsu - OpenCode Session Manager (Issue-Driven)

Usage:
    kugetsu init [--force]                            Initialize base + pm-agent sessions (requires TTY)
    kugetsu start <issue-ref> <message> [--debug]    Start task for issue (forks base session)
    kugetsu continue <issue-ref> [message] [--debug] Continue existing task for issue
    kugetsu delegate <message>                        Send message to PM agent (fire-and-forget)
    kugetsu logs [n]                                  Show recent delegation logs (default: 10)
    kugetsu status                                    Check kugetsu initialization status
    kugetsu doctor [--fix]                            Diagnose and fix kugetsu issues
    kugetsu notify [list|clear]                        Show or clear notifications
    kugetsu list                                     List all tracked sessions
    kugetsu prune [--force]                          Remove orphaned sessions (keeps base + pm-agent)
    kugetsu destroy <issue-ref> [-y]                  Delete session for issue
    kugetsu destroy --pm-agent [-y]                   Delete pm-agent session (not recommended)
    kugetsu destroy --base [-y]                      Delete base session
    kugetsu set-pr <issue-ref> <pr-url>            Set PR URL for session (for PR tracking)
    kugetsu help                                     Show this help

Issue Ref Format:
    instance/user/repo#number
    Example: github.com/shoko/kugetsu#14

Commands:
    init        Create base + pm-agent sessions via TUI. Requires terminal access.
                Use --force to reinitialize if sessions exist.
    start       Fork new session from base for specific issue.
                Requires pm-agent to be running (created by init).
    continue    Continue work on existing issue session.
    delegate    Send message to PM agent for task coordination.
                Fire-and-forget: returns immediately, runs in background.
                Use 'kugetsu logs' to check output.
    logs        Show recent delegation logs.
                Default: 10 most recent. Use 'kugetsu logs 20' for more.
    status      Check if kugetsu is initialized and PM agent is active.
    doctor      Diagnose kugetsu issues. Use --fix to attempt repairs.
    notify      Show or clear notifications from PM agent.
                Use 'kugetsu notify list' to see unread notifications.
    list        Show all sessions (base + pm-agent + forked issues).
    prune       Remove sessions not in index (orphaned from opencode).
                Use --force to skip confirmation.
    destroy     Delete specific issue, pm-agent, or base session.

Options:
    --debug   Show real-time debug output and capture to debug.log

PM Context:
    kugetsu reads ~/.kugetsu/pm-agent.md (if exists) and injects it
    into the PM agent session at init time. This allows customizing PM
    behavior without recreating the session.

Notifications:
    PM Agent writes task completion notifications to ~/.kugetsu/notifications.json
    Use 'kugetsu notify list' to see unread notifications.

Examples:
    kugetsu init
    kugetsu status
    kugetsu delegate "work on issue #5"
    kugetsu logs
    kugetsu logs 20
    kugetsu doctor
    kugetsu doctor --fix
    kugetsu notify list
    kugetsu notify clear
    kugetsu start github.com/shoko/kugetsu#14 "fix bug"
    kugetsu continue github.com/shoko/kugetsu#14 "add tests"
    kugetsu list
EOF
}

ensure_dirs() {
    mkdir -p "$SESSIONS_DIR"
}

ensure_worktree_dir() {
    mkdir -p "$WORKTREES_DIR"
}

issue_ref_to_worktree_name() {
    local issue_ref="$1"
    echo "$issue_ref" | sed 's/[\/:]/-/g' | sed 's/#/-/'
}

issue_ref_to_worktree_path() {
    local issue_ref="$1"
    local parent_dir="${2:-$WORKTREES_DIR}"
    local worktree_name=$(issue_ref_to_worktree_name "$issue_ref")
    echo "$parent_dir/.kugetsu-worktrees/$worktree_name"
}

issue_ref_to_branch_name() {
    local issue_ref="$1"
    local number_part=$(echo "$issue_ref" | grep -oE '#[0-9]+$' || echo "")
    if [ -n "$number_part" ]; then
        echo "fix/issue-${number_part#\#}"
    else
        local identifier=$(echo "$issue_ref" | grep -oE '#[^-]+$' || echo "")
        if [ -n "$identifier" ]; then
            local clean_id=$(echo "$identifier" | sed 's/^#//' | sed 's/-/_/g')
            echo "fix/${clean_id}"
        else
            echo "fix/issue-temp"
        fi
    fi
}

get_repo_url() {
    local issue_ref="$1"
    
    if [ -f "$REPOS_CONFIG" ]; then
        local url=$(python3 -c "import json, sys; d=json.load(open('$REPOS_CONFIG')); print(d.get('$issue_ref', ''))" 2>/dev/null || echo "")
        if [ -n "$url" ]; then
            echo "$url"
            return
        fi
    fi
    
    local instance=$(echo "$issue_ref" | cut -d'/' -f1 | cut -d'#' -f1)
    local rest=$(echo "$issue_ref" | sed 's/.*\///' | sed 's/#.*//')
    
    if [ -n "${GIT_SERVERS[$instance]:-}" ]; then
        echo "${GIT_SERVERS[$instance]}/${rest}.git"
        return
    fi
    
    if [ -n "${GIT_SERVERS[$DEFAULT_GIT_SERVER]:-}" ]; then
        echo "${GIT_SERVERS[$DEFAULT_GIT_SERVER]}/${rest}.git"
        return
    fi
    
    echo "https://${instance}/${rest}.git"
}

worktree_exists() {
    local issue_ref="$1"
    local parent_dir="${2:-$PWD}"
    local worktree_path=$(issue_ref_to_worktree_path "$issue_ref" "$parent_dir")
    [ -d "$worktree_path" ]
}

create_worktree() {
    local issue_ref="$1"
    local parent_dir="${2:-$PWD}"
    local worktree_path=$(issue_ref_to_worktree_path "$issue_ref" "$parent_dir")
    local branch_name=$(issue_ref_to_branch_name "$issue_ref")
    local repo_url=$(get_repo_url "$issue_ref")
    
    if [ -z "$repo_url" ]; then
        echo "Error: Cannot determine repo URL for '$issue_ref'" >&2
        echo "Please add to $REPOS_CONFIG or ensure worktree exists" >&2
        exit 1
    fi
    
    local worktree_parent_dir=$(dirname "$worktree_path")
    mkdir -p "$worktree_parent_dir"
    
    if worktree_exists "$issue_ref" "$parent_dir"; then
        echo "Removing existing worktree at '$worktree_path'..."
        git worktree remove "$worktree_path" 2>/dev/null || rm -rf "$worktree_path"
    fi
    
    echo "Creating worktree at '$worktree_path'..."
    git clone "$repo_url" "$worktree_path" 2>/dev/null || {
        echo "Error: Failed to clone repository" >&2
        exit 1
    }
    
    echo "Creating branch '$branch_name'..."
    (cd "$worktree_path" && git checkout -b "$branch_name" origin/main 2>/dev/null || git checkout -b "$branch_name" main 2>/dev/null) || {
        echo "Warning: Could not checkout branch (may need to run from within worktree after session)" >&2
    }
    
    echo "Worktree created at: $worktree_path"
}

remove_worktree_for_issue() {
    local issue_ref="$1"
    local parent_dir="${2:-$PWD}"
    local worktree_path=$(issue_ref_to_worktree_path "$issue_ref" "$parent_dir")
    
    if worktree_exists "$issue_ref" "$parent_dir"; then
        echo "Removing worktree at '$worktree_path'..."
        git worktree remove "$worktree_path" 2>/dev/null || rm -rf "$worktree_path"
    fi
}

get_worktree_path_for_session() {
    local session_file="$1"
    if [ -f "$session_file" ]; then
        python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', ''))" 2>/dev/null || echo ""
    else
        echo ""
    fi
}

check_pr_status() {
    local pr_url="$1"
    
    if [ -z "$pr_url" ]; then
        echo "no_pr_url"
        return 1
    fi
    
    local hostname=$(echo "$pr_url" | sed -E 's|https://([^/]+)/.*|\1|')
    
    local server_base="${GIT_SERVERS[$hostname]:-}"
    if [ -z "$server_base" ]; then
        echo "unknown_server"
        return 1
    fi
    
    local api_base="${server_base}/api/v1"
    
    local api_url=$(echo "$pr_url" | sed -E 's|https://[^/]+/([^/]+)/([^/]+)/(pulls|merge_requests)/([0-9]+)|'"${api_base}"'/repos/\1/\2/\3/\4|')
    
    local token=""
    if [[ "$hostname" == "github.com" ]]; then
        token="${GITHUB_TOKEN:-}"
    else
        token="${GITEA_TOKEN:-}"
    fi
    
    local response
    if [ -n "$token" ]; then
        response=$(curl -s -H "Authorization: token $token" "$api_url" 2>/dev/null || echo "{}")
    else
        response=$(curl -s "$api_url" 2>/dev/null || echo "{}")
    fi
    
    local state=$(echo "$response" | python3 -c "import json, sys; d=json.load(sys.stdin); print(d.get('state', 'unknown'))" 2>/dev/null || echo "unknown")
    local merged=$(echo "$response" | python3 -c "import json, sys; d=json.load(sys.stdin); print('true' if d.get('merged', False) else 'false')" 2>/dev/null || echo "false")
    
    if [ "$merged" = "true" ]; then
        echo "merged"
    elif [ "$state" = "closed" ]; then
        echo "closed"
    elif [ "$state" = "open" ]; then
        echo "open"
    else
        echo "unknown"
    fi
}

issue_ref_to_filename() {
    local issue_ref="$1"
    echo "$issue_ref" | sed 's/[\/:]/-/g' | sed 's/#/-/'
}

filename_to_issue_ref() {
    local filename="$1"
    local name="${filename%.json}"
    echo "$name" | sed 's/-\([0-9]*\)$/#\1' | sed 's/-/\//g'
}

update_session_pr_url() {
    local issue_ref="$1"
    local pr_url="$2"
    
    if [ -z "$issue_ref" ] || [ -z "$pr_url" ]; then
        echo "Error: update_session_pr_url requires <issue-ref> and <pr-url>" >&2
        return 1
    fi
    
    local session_file=$(get_session_for_issue "$issue_ref")
    if [ -z "$session_file" ] || [ "$session_file" = "null" ]; then
        echo "Error: No session found for '$issue_ref'" >&2
        return 1
    fi
    
    local session_path="$SESSIONS_DIR/$session_file"
    
    if [ ! -f "$session_path" ]; then
        echo "Error: Session file not found: $session_path" >&2
        return 1
    fi
    
    python3 << PYEOF
import json

session_path = "$session_path"
pr_url = "$pr_url"

with open(session_path, 'r') as f:
    session = json.load(f)

session['pr_url'] = pr_url

with open(session_path, 'w') as f:
    json.dump(session, f, indent=2)

print(f"Updated pr_url to: {pr_url}")
PYEOF
}

read_index() {
    if [ -f "$INDEX_FILE" ]; then
        cat "$INDEX_FILE"
    else
        echo '{"base": null, "pm_agent": null, "issues": {}}'
    fi
}

write_index() {
    local base="$1"
    local pm_agent="$2"
    local issues_json="$3"
    local temp_file="$INDEX_FILE.tmp.$$"
    printf '{"base": %s, "pm_agent": %s, "issues": %s}\n' "$base" "$pm_agent" "$issues_json" > "$temp_file"
    mv "$temp_file" "$INDEX_FILE"
}

get_base_session_id() {
    local index=$(read_index)
    echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('base') or '')"
}

get_pm_agent_session_id() {
    local index=$(read_index)
    echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('pm_agent') or '')"
}

get_session_for_issue() {
    local issue_ref="$1"
    local index=$(read_index)
    echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d['issues'].get('$issue_ref') or '')"
}

set_base_in_index() {
    local base_session_id="$1"
    local pm_agent=$(get_pm_agent_session_id)
    local issues_json=$(read_index | python3 -c "import sys, json; d=json.load(sys.stdin); print(json.dumps(d['issues']))")
    if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ]; then
        write_index "\"$base_session_id\"" "null" "$issues_json"
    else
        write_index "\"$base_session_id\"" "\"$pm_agent\"" "$issues_json"
    fi
}

set_pm_agent_in_index() {
    local pm_agent_session_id="$1"
    local base=$(get_base_session_id)
    local issues_json=$(read_index | python3 -c "import sys, json; d=json.load(sys.stdin); print(json.dumps(d['issues']))")
    if [ -z "$base" ] || [ "$base" = "null" ]; then
        write_index "null" "\"$pm_agent_session_id\"" "$issues_json"
    else
        write_index "\"$base\"" "\"$pm_agent_session_id\"" "$issues_json"
    fi
}

add_issue_to_index() {
    local issue_ref="$1"
    local session_file="$2"
    local index=$(read_index)
    local base=$(get_base_session_id)
    local pm_agent=$(get_pm_agent_session_id)
    local issues=$(echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); print(json.dumps(d['issues']))")
    local new_issues=$(echo "$issues" | python3 -c "import sys, json; d=json.load(sys.stdin); d['$issue_ref']='$session_file'; print(json.dumps(d))")
    if [ -z "$base" ] || [ "$base" = "null" ]; then
        if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ]; then
            write_index "null" "null" "$new_issues"
        else
            write_index "null" "\"$pm_agent\"" "$new_issues"
        fi
    else
        if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ]; then
            write_index "\"$base\"" "null" "$new_issues"
        else
            write_index "\"$base\"" "\"$pm_agent\"" "$new_issues"
        fi
    fi
}

remove_issue_from_index() {
    local issue_ref="$1"
    local index=$(read_index)
    local base=$(get_base_session_id)
    local pm_agent=$(get_pm_agent_session_id)
    local new_issues=$(echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); d['issues'].pop('$issue_ref', None); print(json.dumps(d['issues']))")
    if [ -z "$base" ] || [ "$base" = "null" ]; then
        if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ]; then
            write_index "null" "null" "$new_issues"
        else
            write_index "null" "\"$pm_agent\"" "$new_issues"
        fi
    else
        if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ]; then
            write_index "\"$base\"" "null" "$new_issues"
        else
            write_index "\"$base\"" "\"$pm_agent\"" "$new_issues"
        fi
    fi
}

validate_issue_ref() {
    local issue_ref="$1"
    if [[ ! "$issue_ref" =~ ^[^/]+/[^/]+/[^#]+#[0-9]+$ ]]; then
        echo "Error: invalid issue ref format" >&2
        echo "Expected: instance/user/repo#number" >&2
        echo "Example: github.com/shoko/kugetsu#14" >&2
        exit 1
    fi
}

check_opencode_session_exists() {
    local session_id="$1"
    opencode session list --format json 2>/dev/null | grep -q "\"$session_id\""
}

kugetsu_get_pm_context() {
    local user_pm_context="${KUGETSU_DIR}/pm-agent.md"
    local skill_pm_context="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../pm/SKILL.md"
    
    if [ -f "$user_pm_context" ]; then
        cat "$user_pm_context"
    elif [ -f "$skill_pm_context" ]; then
        cat "$skill_pm_context"
    else
        echo ""
    fi
}

kugetsu_get_fork_context() {
    local issue_ref="$1"
    local context=""
    
    context="## IMPORTANT WORKING RULES

1. You are working on issue: $issue_ref
2. If you encounter ANY error, blocker, or cannot complete the task:
   - STOP immediately
   - Log what happened and why you cannot proceed
   - Do NOT switch to other work or try alternative approaches
3. Do NOT work on other issues or PRs unless explicitly asked
4. Environment variables are available in ~/.kugetsu/env/

"
    
    if [ -f "$REPOS_CONFIG" ]; then
        context="${context}
## REPOSITORIES CONFIG
$(cat "$REPOS_CONFIG")

"
    fi
    
    if [ -f "$ENV_DIR/default.env" ]; then
        context="${context}
## ENVIRONMENT (available at ~/.kugetsu/env/)
Environment file exists at: $ENV_DIR/default.env
Source it with: source ~/.kugetsu/env/default.env
"
    fi
    
    echo "$context"
}

kugetsu_add_notification() {
    local type="$1"
    local message="$2"
    local issue_ref="${3:-}"
    local gitea_url="${4:-}"
    
    mkdir -p "$(dirname "$NOTIFICATIONS_FILE")"
    
    local notification=$(python3 << PYEOF
import json
import os
from datetime import datetime

notification = {
    "type": "$type",
    "message": "$message",
    "issue_ref": "$issue_ref" if "$issue_ref" else None,
    "gitea_url": "$gitea_url" if "$gitea_url" else None,
    "timestamp": datetime.now().isoformat(),
    "read": False
}

file_path = os.path.expanduser("$NOTIFICATIONS_FILE")
notifications = []

if os.path.exists(file_path):
    try:
        with open(file_path, 'r') as f:
            notifications = json.load(f)
    except:
        notifications = []

notifications.append(notification)

with open(file_path, 'w') as f:
    json.dump(notifications, f, indent=2)

print("Notification added")
PYEOF
)
    echo "$notification"
}

kugetsu_get_notifications() {
    local limit="${1:-10}"
    
    if [ ! -f "$NOTIFICATIONS_FILE" ]; then
        echo "[]"
        return
    fi
    
    python3 << PYEOF
import json
import os
from datetime import datetime

file_path = os.path.expanduser("$NOTIFICATIONS_FILE")

if not os.path.exists(file_path):
    print("[]")
    exit(0)

try:
    with open(file_path, 'r') as f:
        notifications = json.load(f)
    
    unread = [n for n in notifications if not n.get("read", False)]
    unread.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
    
    for n in unread[:$limit]:
        ts = n.get("timestamp", "unknown")
        ntype = n.get("type", "info")
        msg = n.get("message", "")
        issue = n.get("issue_ref", "")
        gitea = n.get("gitea_url", "")
        
        print(f"[{ts}] {ntype}: {msg}")
        if issue:
            print(f"  Issue: {issue}")
        if gitea:
            print(f"  Link: {gitea}")
        print()
    
    if not unread:
        print("No unread notifications.")
    
except Exception as e:
    print(f"Error reading notifications: {e}")
PYEOF
}

kugetsu_clear_notifications() {
    if [ ! -f "$NOTIFICATIONS_FILE" ]; then
        return
    fi
    
    python3 << PYEOF
import json
import os

file_path = os.path.expanduser("$NOTIFICATIONS_FILE")

if not os.path.exists(file_path):
    exit(0)

try:
    with open(file_path, 'r') as f:
        notifications = json.load(f)
    
    for n in notifications:
        n["read"] = True
    
    with open(file_path, 'w') as f:
        json.dump(notifications, f, indent=2)
    
    print("Notifications marked as read")
except Exception as e:
    print(f"Error: {e}")
PYEOF
}

cmd_notify() {
    local action="${1:-}"
    
    case "$action" in
        ""|"list"|"show")
            kugetsu_get_notifications 10
            ;;
        "clear")
            kugetsu_clear_notifications
            ;;
        *)
            echo "Usage: kugetsu notify [list|clear]"
            ;;
    esac
}

cmd_status() {
    if [ ! -f "$INDEX_FILE" ]; then
        echo "kugetsu_not_initialized"
        return
    fi
    
    local base=$(get_base_session_id)
    local pm_agent=$(get_pm_agent_session_id)
    
    if [ -z "$base" ] || [ "$base" = "null" ]; then
        echo "base_session_missing"
        return
    fi
    
    if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ] || [ "$pm_agent" = "None" ]; then
        echo "pm_agent_missing"
        return
    fi
    
    echo "ok"
}

get_verbosity_context() {
    local verbosity="${KUGETSU_VERBOSITY:-default}"
    local verbosity_file="$VERBOSITY_DIR/${verbosity}.md"
    
    if [ -f "$verbosity_file" ]; then
        cat "$verbosity_file"
    else
        echo "## Verbosity: $verbosity"
    fi
}

init_verbosity_templates() {
    mkdir -p "$VERBOSITY_DIR"
    
    if [ ! -f "$VERBOSITY_DIR/verbose.md" ]; then
        cat > "$VERBOSITY_DIR/verbose.md" << 'EOF'
## Verbosity: Verbose

You are operating in HIGH verbosity mode. Include ALL available context:
- Full command outputs and their results
- Detailed reasoning and thinking process
- All file changes with diffs when relevant
- Complete log excerpts
- Comprehensive status updates
- Ask clarifying questions when uncertain
EOF
    fi
    
    if [ ! -f "$VERBOSITY_DIR/default.md" ]; then
        cat > "$VERBOSITY_DIR/default.md" << 'EOF'
## Verbosity: Default

You are operating in NORMAL verbosity mode. Provide balanced output:
- Standard command outputs and key results
- Moderate reasoning detail
- Important file changes summarized
- Regular status updates
EOF
    fi
    
    if [ ! -f "$VERBOSITY_DIR/quiet.md" ]; then
        cat > "$VERBOSITY_DIR/quiet.md" << 'EOF'
## Verbosity: Quiet

You are operating in QUIET verbosity mode. Keep output minimal:
- Only essential information
- Brief status updates (1-2 sentences)
- Final decisions only
- Yes/No answers when appropriate
EOF
    fi
}

parse_issue_ref_from_message() {
    local message="$1"
    
    local gitserver=""
    local owner=""
    local repo=""
    local issue_number=""
    
    if echo "$message" | grep -qE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(issues|pull)/[0-9]+'; then
        gitserver=$(echo "$message" | grep -oE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+' | head -1 | sed 's/\/[^/]*\/[^/]*$//')
        local full_path=$(echo "$message" | grep -oE '[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(issues|pull)/[0-9]+' | head -1)
        owner=$(echo "$full_path" | cut -d'/' -f2)
        repo=$(echo "$full_path" | cut -d'/' -f3)
        issue_number=$(echo "$full_path" | grep -oE '[0-9]+$' | head -1)
    elif echo "$message" | grep -qE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#([0-9]+)'; then
        owner=$(echo "$message" | grep -oE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#' | sed 's/#$//' | cut -d'/' -f1)
        repo=$(echo "$message" | grep -oE '[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#' | sed 's/#$//' | cut -d'/' -f2)
        issue_number=$(echo "$message" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -1)
    fi
    
    echo "${gitserver}|${owner}|${repo}|${issue_number}"
}

get_missing_info() {
    local parsed="$1"
    local gitserver=$(echo "$parsed" | cut -d'|' -f1)
    local owner=$(echo "$parsed" | cut -d'|' -f2)
    local repo=$(echo "$parsed" | cut -d'|' -f3)
    local issue_number=$(echo "$parsed" | cut -d'|' -f4)
    
    local missing=""
    [ -z "$gitserver" ] && missing="${missing}git server, "
    [ -z "$owner" ] && missing="${missing}owner, "
    [ -z "$repo" ] && missing="${missing}repository, "
    [ -z "$issue_number" ] && missing="${missing}issue number, "
    
    echo "$missing" | sed 's/, $//'
}

build_missing_info_context() {
    local missing="$1"
    if [ -n "$missing" ]; then
        echo ""
        echo "NOTE: This task delegation has no information about: ${missing}."
        echo "We need them if user wants to work on a specific issue. Otherwise we don't need it."
    fi
}

find_worktrees_by_issue_number() {
    local issue_number="$1"
    local results=""
    
    if [ ! -d "$WORKTREES_DIR/.kugetsu-worktrees" ]; then
        echo ""
        return
    fi
    
    for wt in "$WORKTREES_DIR/.kugetsu-worktrees"/*; do
        if [ -d "$wt" ]; then
            local wt_issue_number=$(echo "$wt" | grep -oE '#?[0-9]+$' | grep -oE '[0-9]+' | head -1)
            if [ "$wt_issue_number" = "$issue_number" ]; then
                results="${results}${wt}:worktree
"
            fi
        fi
    done
    
    echo "$results"
}

find_sessions_by_issue_number() {
    local issue_number="$1"
    local results=""
    
    if [ ! -d "$SESSIONS_DIR" ]; then
        echo ""
        return
    fi
    
    for session_file in "$SESSIONS_DIR"/*.json; do
        if [ -f "$session_file" ]; then
            local session_issue_ref=$(basename "$session_file" .json | sed 's/_/\//g')
            local session_issue_number=$(echo "$session_issue_ref" | grep -oE '#?[0-9]+$' | grep -oE '[0-9]+' | head -1)
            if [ "$session_issue_number" = "$issue_number" ]; then
                results="${results}${session_file}:session
"
            fi
        fi
    done
    
    echo "$results"
}

cmd_delegate() {
    local message="${1:-}"
    local verbosity="${KUGETSU_VERBOSITY:-default}"
    
    if [ -z "$message" ]; then
        echo "Error: message is required" >&2
        echo "Usage: kugetsu delegate <message>" >&2
        exit 1
    fi
    
    local pm_session=$(get_pm_agent_session_id)
    if [ -z "$pm_session" ] || [ "$pm_session" = "null" ] || [ "$pm_session" = "None" ]; then
        echo "Error: PM agent session not found. Run 'kugetsu init' first." >&2
        exit 1
    fi
    
    mkdir -p "$LOGS_DIR"
    local log_file="$LOGS_DIR/delegate-$(date +%s).log"
    
    local parsed=$(parse_issue_ref_from_message "$message")
    local gitserver=$(echo "$parsed" | cut -d'|' -f1)
    local owner=$(echo "$parsed" | cut -d'|' -f2)
    local repo=$(echo "$parsed" | cut -d'|' -f3)
    local issue_number=$(echo "$parsed" | cut -d'|' -f4)
    
    local missing_info=$(get_missing_info "$parsed")
    local context_injection=""
    if [ -n "$missing_info" ]; then
        context_injection=$(build_missing_info_context "$missing_info")
        echo "NOTE: Delegation missing information: ${missing_info}"
    fi
    
    local candidates=""
    local candidate_count=0
    
    if [ -n "$issue_number" ]; then
        local worktrees=$(find_worktrees_by_issue_number "$issue_number")
        local sessions=$(find_sessions_by_issue_number "$issue_number")
        
        while IFS=: read -r path type; do
            if [ -n "$path" ]; then
                candidate_count=$((candidate_count + 1))
                candidates="${candidates}${candidate_count}) ${path} (${type})
"
            fi
        done <<< "$worktrees"
        
        while IFS=: read -r path type; do
            if [ -n "$path" ]; then
                candidate_count=$((candidate_count + 1))
                candidates="${candidates}${candidate_count}) ${path} (${type})
"
            fi
        done <<< "$sessions"
    fi
    
    local use_worktree=""
    if [ $candidate_count -gt 0 ]; then
        echo "Found $candidate_count existing worktree(s)/session(s) for issue #${issue_number}:"
        echo "$candidates"
        echo "r) Delegate anyway (without routing)"
        echo "Which one to use? [1-${candidate_count}/r]: "
        read -r choice
        
        if [ "$choice" = "r" ] || [ -z "$choice" ]; then
            use_worktree=""
        elif [ "$choice" -ge 1 ] && [ "$choice" -le "$candidate_count" ]; then
            local selected=$(echo "$candidates" | sed -n "${choice}p")
            use_worktree=$(echo "$selected" | sed 's/) .*//')
        fi
    fi
    
    local final_message="${message}${context_injection}"
    
    if [ -n "$use_worktree" ]; then
        if [ -d "$use_worktree" ]; then
            echo "Using worktree: $use_worktree"
            final_message="${final_message}

NOTE: Worktree selected: ${use_worktree}"
        fi
    fi
    
    local temp_dir="${KUGETSU_TEMP_DIR:-$HOME/.local/share/opencode/tool-output}"
    
    mkdir -p "$ENV_DIR"
    local env_sh="set -a; export KUGETSU_TEMP_DIR='$temp_dir'; export KUGETSU_VERBOSITY='$verbosity'; "
    if [ -f "$ENV_DIR/pm-agent.env" ]; then
        env_sh="${env_sh}source '$ENV_DIR/pm-agent.env'; "
    elif [ -f "$ENV_DIR/default.env" ]; then
        env_sh="${env_sh}source '$ENV_DIR/default.env'; "
    fi
    env_sh="${env_sh}set +a; "
    
    nohup sh -c "${env_sh}opencode run '${final_message}' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 &
    disown
    echo "Delegated to PM agent (logged to $(basename "$log_file"))"
    echo "Verbosity: $verbosity"
}

cmd_logs() {
    local count="${1:-10}"
    
    if [ ! -d "$LOGS_DIR" ]; then
        echo "No logs found."
        return
    fi
    
    # Log rotation: delete logs older than 7 days
    find "$LOGS_DIR" -type f -mtime +7 -delete 2>/dev/null
    
    ls -lt "$LOGS_DIR" | head -$((count + 1)) | tail -$count | while read line; do
        echo "$line"
    done
}

cmd_env() {
    local action="${1:-}"
    local agent_type="${2:-}"
    
    mkdir -p "$ENV_DIR"
    
    case "$action" in
        ""|"list")
            echo "Environment files in $ENV_DIR:"
            if [ -d "$ENV_DIR" ]; then
                for f in "$ENV_DIR"/*.env; do
                    if [ -f "$f" ]; then
                        echo "  $(basename "$f")"
                    fi
                done
            fi
            if [ ! -d "$ENV_DIR" ] || [ -z "$(ls -A "$ENV_DIR"/*.env 2>/dev/null)" ]; then
                echo "  (no env files found)"
            fi
            ;;
        "show")
            local file="$ENV_DIR/${agent_type:-default}.env"
            if [ -f "$file" ]; then
                echo "=== $file ==="
                while IFS= read -r line; do
                    echo "$(mask_sensitive_vars "$line")"
                done < "$file"
            else
                echo "No env file for: ${agent_type:-default}"
            fi
            ;;
        "set")
            local key="${2:-}"
            local value="${3:-}"
            local target="${4:-default}"
            if [ -z "$key" ] || [ -z "$value" ]; then
                echo "Usage: kugetsu env set <key> <value> [agent]" >&2
                echo "  agent: default, pm-agent, or issue ref" >&2
                exit 1
            fi
            local file="$ENV_DIR/${target}.env"
            if [ -f "$file" ]; then
                if grep -q "^${key}=" "$file"; then
                    sed -i "s|^${key}=.*|${key}=\"${value}\"|" "$file"
                else
                    echo "${key}=\"${value}\"" >> "$file"
                fi
            else
                echo "${key}=\"${value}\"" > "$file"
            fi
            echo "Set ${key}=${value} in ${target}.env"
            ;;
        "get")
            local key="${2:-}"
            local target="${3:-default}"
            local file="$ENV_DIR/${target}.env"
            if [ -z "$key" ]; then
                echo "Usage: kugetsu env get <key> [agent]" >&2
                exit 1
            fi
            if [ -f "$file" ]; then
                local val=$(grep "^${key}=" "$file" | cut -d'=' -f2 | tr -d '"')
                if [ -n "$val" ]; then
                    echo "$val"
                else
                    echo "Key '$key' not found in ${target}.env" >&2
                    exit 1
                fi
            else
                echo "No env file for: ${target}" >&2
                exit 1
            fi
            ;;
        "rm"|"remove"|"delete")
            local key="${2:-}"
            local target="${3:-default}"
            if [ -z "$key" ]; then
                echo "Usage: kugetsu env rm <key> [agent]" >&2
                exit 1
            fi
            local file="$ENV_DIR/${target}.env"
            if [ -f "$file" ]; then
                grep -v "^${key}=" "$file" > "$file.tmp" && mv "$file.tmp" "$file"
                echo "Removed $key from ${target}.env"
            fi
            ;;
        *)
            echo "Usage: kugetsu env <list|show|set|get|rm> [args]" >&2
            echo "" >&2
            echo "Commands:" >&2
            echo "  list              List all env files" >&2
            echo "  show [agent]      Show env file contents (masked)" >&2
            echo "  set <k> <v> [a]  Set key=value in agent env (default/pm-agent)" >&2
            echo "  get <key> [a]    Get value for key" >&2
            echo "  rm <key> [a]     Remove key from agent env" >&2
            exit 1
            ;;
    esac
}

cmd_doctor() {
    local fix=false
    local fix_permissions=false
    
    while [ $# -gt 0 ]; do
        case "$1" in
            --fix)
                fix=true
                ;;
            --fix-permissions)
                fix_permissions=true
                ;;
            *)
                ;;
        esac
        shift
    done
    
    echo "=== kugetsu doctor ==="
    echo ""
    
    local issues=0
    
    if [ ! -f "$INDEX_FILE" ]; then
        echo "[ISSUE] kugetsu not initialized (index.json missing)"
        issues=$((issues + 1))
    else
        echo "[OK] kugetsu initialized"
        
        local base=$(get_base_session_id)
        if [ -z "$base" ] || [ "$base" = "null" ]; then
            echo "[ISSUE] Base session missing"
            issues=$((issues + 1))
        else
            echo "[OK] Base session: $base"
        fi
        
        local pm_agent=$(get_pm_agent_session_id)
        if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ] || [ "$pm_agent" = "None" ]; then
            echo "[ISSUE] PM agent session missing"
            issues=$((issues + 1))
        else
            echo "[OK] PM agent: $pm_agent"
        fi
        
        local pm_context_file="${KUGETSU_DIR}/pm-agent.md"
        if [ -f "$pm_context_file" ]; then
            echo "[OK] PM context file exists"
        else
            echo "[INFO] PM context file not found (optional): $pm_context_file"
        fi
    fi
    
    echo ""
    if [ $issues -eq 0 ]; then
        echo "No issues found."
    else
        echo "Found $issues issue(s)."
    fi
    
    if [ "$fix" = true ] && [ $issues -gt 0 ]; then
        echo ""
        echo "Running fixes..."
        
        if [ ! -f "$INDEX_FILE" ]; then
            echo "Cannot fix: not initialized. Run 'kugetsu init' first."
        else
            local pm_agent=$(get_pm_agent_session_id)
            if [ -n "$pm_agent" ] && [ "$pm_agent" != "null" ] && [ "$pm_agent" != "None" ]; then
                echo "[FIX] Recreating PM agent session..."
                local base=$(get_base_session_id)
                if [ -n "$base" ] && [ "$base" != "null" ]; then
                    rm -f "$SESSIONS_DIR/pm-agent.json"
                    
                    local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
                    local before_set="${before_sessions//$'\n'/|}"
                    
                    local pm_context=$(kugetsu_get_pm_context)
                    if [ -n "$pm_context" ]; then
                        opencode run "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. $pm_context" --fork --session "$base" 2>&1 || true
                    else
                        opencode run "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions." --fork --session "$base" 2>&1 || true
                    fi
                    
                    local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
                    local new_pm_session_id=""
                    while IFS= read -r sess; do
                        if [[ ! "$before_set" =~ \|${sess}\| ]] && [[ "$sess" != "$base" ]]; then
                            new_pm_session_id="$sess"
                            break
                        fi
                    done <<< "$after_sessions"
                    
                    if [ -n "$new_pm_session_id" ]; then
                        printf '{"type": "pm_agent", "opencode_session_id": "%s", "created_at": "%s", "state": "idle"}\n' \
                            "$new_pm_session_id" "$(date -Iseconds)" > "$SESSIONS_DIR/pm-agent.json"
                        set_pm_agent_in_index "$new_pm_session_id"
                        echo "[FIX] PM agent recreated: $new_pm_session_id"
                    else
                        echo "[FIX] Warning: Could not detect new PM session ID"
                    fi
                else
                    echo "[FIX] Cannot recreate PM agent: base session missing"
                fi
            else
                echo "[FIX] Cannot fix: PM agent not initialized. Run 'kugetsu init' first."
            fi
        fi
    fi
    
    if [ "$fix_permissions" = true ]; then
        echo ""
        echo "Fixing session permissions..."
        fix_session_permissions
    fi
}

fix_session_permissions() {
    local opencode_db="${OPENCODE_DB:-$HOME/.local/share/opencode/opencode.db}"
    
    if [ ! -f "$opencode_db" ]; then
        echo "[ERROR] opencode database not found: $opencode_db"
        return 1
    fi
    
    local base_session_id=$(get_base_session_id)
    local pm_agent_session_id=$(get_pm_agent_session_id)
    
    local PERMISSION_JSON='[{"permission":"question","pattern":"*","action":"deny"},{"permission":"plan_enter","pattern":"*","action":"deny"},{"permission":"plan_exit","pattern":"*","action":"deny"},{"permission":"external_directory","pattern":"*","action":"allow"}]'
    
    if [ -n "$base_session_id" ] && [ "$base_session_id" != "null" ]; then
        echo "Updating base session permissions: $base_session_id"
        python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
cursor.execute(\"UPDATE session SET permission = ? WHERE id = ?\", ('$PERMISSION_JSON', '$base_session_id'))
conn.commit()
print('[OK] Base session permissions updated')
"
    fi
    
    if [ -n "$pm_agent_session_id" ] && [ "$pm_agent_session_id" != "null" ] && [ "$pm_agent_session_id" != "None" ]; then
        echo "Updating PM agent session permissions: $pm_agent_session_id"
        python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
cursor.execute(\"UPDATE session SET permission = ? WHERE id = ?\", ('$PERMISSION_JSON', '$pm_agent_session_id'))
conn.commit()
print('[OK] PM agent session permissions updated')
"
    fi
    
    echo "Session permissions fix complete"
}

DEBUG_MODE=false

set_debug_mode() {
    DEBUG_MODE=false
    local filtered_args=()
    while [ $# -gt 0 ]; do
        case "$1" in
            --debug)
                DEBUG_MODE=true
                ;;
            *)
                filtered_args+=("$1")
                ;;
        esac
        shift
    done
    echo "${filtered_args[@]}"
}

cmd_server() {
    local action="${1:-}"
    
    case "$action" in
        ""|"list")
            if [ -z "${GIT_SERVERS+x}" ]; then
                echo "No git servers configured"
                return
            fi
            echo "Git servers:"
            for key in "${!GIT_SERVERS[@]}"; do
                local marker=""
                if [ "$key" = "$DEFAULT_GIT_SERVER" ]; then
                    marker=" (default)"
                fi
                echo "  $key -> ${GIT_SERVERS[$key]}$marker"
            done
            ;;
        "add")
            local name="${2:-}"
            local url="${3:-}"
            if [ -z "$name" ] || [ -z "$url" ]; then
                echo "Usage: kugetsu server add <name> <url>" >&2
                exit 1
            fi
            if grep -q "^GIT_SERVERS\[" "$KUGETSU_DIR/config" 2>/dev/null; then
                sed -i "s|^GIT_SERVERS\[\"$name\"\]=.*|GIT_SERVERS[\"$name\"]=\"$url\"|" "$KUGETSU_DIR/config"
                if ! grep -q "GIT_SERVERS\[\"$name\"\]" "$KUGETSU_DIR/config" 2>/dev/null; then
                    sed -i "/^declare -A GIT_SERVERS/a GIT_SERVERS[\"$name\"]=\"$url\"" "$KUGETSU_DIR/config"
                fi
            else
                echo "declare -A GIT_SERVERS" >> "$KUGETSU_DIR/config"
                echo "GIT_SERVERS[\"$name\"]=\"$url\"" >> "$KUGETSU_DIR/config"
            fi
            source "$KUGETSU_DIR/config"
            echo "Added git server: $name -> $url"
            ;;
        "remove"|"rm"|"delete")
            local name="${2:-}"
            if [ -z "$name" ]; then
                echo "Usage: kugetsu server remove <name>" >&2
                exit 1
            fi
            if [ -n "${GIT_SERVERS[$name]:-}" ]; then
                if [ "$name" = "$DEFAULT_GIT_SERVER" ]; then
                    echo "Error: Cannot remove default server. Set a new default first." >&2
                    exit 1
                fi
                sed -i "/GIT_SERVERS\[\"$name\"\]/d" "$KUGETSU_DIR/config" 2>/dev/null
                source "$KUGETSU_DIR/config"
                echo "Removed git server: $name"
            else
                echo "Error: Server '$name' not found" >&2
                exit 1
            fi
            ;;
        "default")
            local name="${2:-}"
            if [ -z "$name" ]; then
                echo "Current default: $DEFAULT_GIT_SERVER"
                return
            fi
            if [ -n "${GIT_SERVERS[$name]:-}" ]; then
                sed -i "s/^DEFAULT_GIT_SERVER=.*/DEFAULT_GIT_SERVER=\"$name\"/" "$KUGETSU_DIR/config"
                source "$KUGETSU_DIR/config"
                echo "Set default git server to: $name"
            else
                echo "Error: Server '$name' not found" >&2
                exit 1
            fi
            ;;
        "get")
            local name="${2:-$DEFAULT_GIT_SERVER}"
            if [ -n "${GIT_SERVERS[$name]:-}" ]; then
                echo "${GIT_SERVERS[$name]}"
            else
                echo "Error: Server '$name' not found" >&2
                exit 1
            fi
            ;;
        *)
            echo "Usage: kugetsu server <list|add|remove|default|get>" >&2
            echo "" >&2
            echo "Commands:" >&2
            echo "  list              List all configured git servers" >&2
            echo "  add <name> <url>  Add a new git server" >&2
            echo "  remove <name>     Remove a git server" >&2
            echo "  default [<name>]  Get or set default server" >&2
            echo "  get [<name>]      Get URL for a server (default: current default)" >&2
            exit 1
            ;;
    esac
}

cmd_init() {
    local force=false

    while [ $# -gt 0 ]; do
        case "$1" in
            --force)
                force=true
                ;;
            *)
                ;;
        esac
        shift
    done

    ensure_dirs

    if [ ! -f "$KUGETSU_DIR/config" ] || [ "$force" = true ]; then
        cat > "$KUGETSU_DIR/config" << 'EOF'
# User configuration overrides
# Values set here take precedence over defaults
# Changes take effect immediately (no re-init needed)

# Max concurrent dev agents (default: 3)
# MAX_CONCURRENT_AGENTS=5

# Verbosity level for PM agent output (verbose, default, or quiet)
# KUGETSU_VERBOSITY=default

# Git server configurations
# Format: GIT_SERVERS["hostname"]="https://hostname"
# Add servers with: kugetsu server add <name> <url>
declare -A GIT_SERVERS
GIT_SERVERS["github.com"]="https://github.com"
DEFAULT_GIT_SERVER="github.com"
EOF
        echo "Created config file: $KUGETSU_DIR/config"
    fi

    mkdir -p "$ENV_DIR"
    if [ ! -f "$ENV_DIR/default.env" ]; then
        cat > "$ENV_DIR/default.env" << 'EOF'
# Default environment variables for all agents
# Variables here are exported to subagents
# Use 'export' prefix for variables that subagents need
# Example:
# export GITEA_TOKEN=your_token_here
EOF
        echo "Created default env file: $ENV_DIR/default.env"
    fi
    if [ ! -f "$ENV_DIR/pm-agent.env" ]; then
        cat > "$ENV_DIR/pm-agent.env" << 'EOF'
# PM Agent environment variables
# These override default.env for the PM agent
# Use 'export' prefix for variables that subagents need
# Example:
# export GITEA_TOKEN=your_gitea_token_here
EOF
        echo "Created pm-agent env file: $ENV_DIR/pm-agent.env"
    fi

    if [ -d "$LOGS_DIR" ]; then
        echo "Cleaning up old logs..."
        rm -rf "$LOGS_DIR"/*.log 2>/dev/null || true
    fi

    local existing_base=$(get_base_session_id)
    local existing_pm=$(get_pm_agent_session_id)
    
    if [ -n "$existing_base" ] && [ "$existing_base" != "null" ]; then
        if [ "$force" = true ]; then
            echo "Warning: Reinitializing sessions (force mode)" >&2
        else
            echo "Error: Base session already exists: $existing_base" >&2
            echo "Use --force to reinitialize" >&2
            exit 1
        fi
    fi

    if ! test -t 0; then
        echo "Error: init requires a terminal (TTY)" >&2
        echo "Please run this command in an interactive shell" >&2
        exit 1
    fi

    local init_worktree_dir="$HOME/.kugetsu-worktrees"
    mkdir -p "$init_worktree_dir"
    cd "$init_worktree_dir"
    echo "Initialized kugetsu worktrees directory: $init_worktree_dir"
    echo "Base session will be created in this directory."
    echo ""

    local cwd_files=$(ls -A "$PWD" 2>/dev/null | wc -l)
    local cwd_git=$(git rev-parse --is-inside-work-tree 2>/dev/null || echo "false")
    if [ "$cwd_files" -gt 0 ] || [ "$cwd_git" = "true" ]; then
        echo "Warning: Worktrees directory is not empty: $PWD" >&2
        echo "This may cause project context to contaminate the base session." >&2
        echo "Consider running kugetsu destroy --base -y and reinitializing." >&2
        echo "" >&2
        echo "Files in current directory: $cwd_files" >&2
        if [ "$cwd_git" = "true" ]; then
            echo "Git repository detected: $(git rev-parse --show-toplevel 2>/dev/null || echo 'unknown')" >&2
        fi
        echo "" >&2
        echo "Press Ctrl+C to cancel or wait 5 seconds to continue anyway..." >&2
        sleep 5
    fi

    echo "Starting TUI to create base session..."
    echo "Press Ctrl+C to cancel or wait for session to be created"
    sleep 2

    if ! opencode; then
        echo "Error: opencode TUI failed to start" >&2
        echo "Please ensure opencode is installed and accessible" >&2
        exit 1
    fi

    local session_ids=$(opencode session list 2>/dev/null | grep -E '^ses_' | awk '{print $1}' | tail -1)
    if [ -z "$session_ids" ]; then
        echo "Error: Could not find newly created session" >&2
        exit 1
    fi

    local new_session_id=$(echo "$session_ids" | tail -1)
    local session_file="base.json"

    printf '{"type": "base", "opencode_session_id": "%s", "created_at": "%s", "state": "idle"}\n' \
        "$new_session_id" "$(date -Iseconds)" > "$SESSIONS_DIR/$session_file"

    set_base_in_index "$new_session_id"
    echo "Base session initialized: $new_session_id"

    echo ""
    echo "Creating PM agent session..."
    sleep 1
    
    local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
    local before_set="${before_sessions//$'\n'/|}"
    
    local pm_context=$(kugetsu_get_pm_context)
    local pm_prompt="You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions."
    if [ -n "$pm_context" ]; then
        pm_prompt="You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. $pm_context"
    fi
    
    # Set GIT_EDITOR to cat for non-interactive git operations (rebase, etc.)
    export GIT_EDITOR=cat
    export EDITOR=cat
    
    opencode run "$pm_prompt" --fork --session "$new_session_id" 2>&1 || true
    
    local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
    local new_pm_session_id=""
    while IFS= read -r sess; do
        if [[ ! "$before_set" =~ \|${sess}\| ]] && [[ "$sess" != "$new_session_id" ]]; then
            new_pm_session_id="$sess"
            break
        fi
    done <<< "$after_sessions"
    
    if [ -z "$new_pm_session_id" ]; then
        echo "Warning: Could not detect PM agent session ID. It may still have been created." >&2
    else
        local pm_session_file="pm-agent.json"
        printf '{"type": "pm_agent", "opencode_session_id": "%s", "created_at": "%s", "state": "idle"}\n' \
            "$new_pm_session_id" "$(date -Iseconds)" > "$SESSIONS_DIR/$pm_session_file"
        set_pm_agent_in_index "$new_pm_session_id"
        echo "PM agent session initialized: $new_pm_session_id"
    fi
    
    echo ""
    echo "Initialization complete!"
    echo "- Base session: $new_session_id"
    echo "- PM agent: ${new_pm_session_id:-created by hermes}"
    
    fix_session_permissions
}

cmd_start() {
    local issue_ref=""
    local message=""
    local pr_url=""
    local args=("$@")

    args=$(set_debug_mode "${args[@]}")

    for arg in $args; do
        if [ -z "$issue_ref" ]; then
            issue_ref="$arg"
        elif [ -z "$message" ]; then
            message="$arg"
        elif [ -z "$pr_url" ]; then
            pr_url="$arg"
        fi
    done

    if [ -z "$issue_ref" ] || [ -z "$message" ]; then
        echo "Error: start requires <issue-ref> and <message>" >&2
        echo "Usage: kugetsu start <issue-ref> <message> [pr-url]" >&2
        exit 1
    fi

    validate_issue_ref "$issue_ref"
    ensure_dirs

    local base_session_id=$(get_base_session_id)
    if [ -z "$base_session_id" ] || [ "$base_session_id" = "null" ]; then
        echo "Error: No base session. Run 'kugetsu init' first." >&2
        exit 1
    fi

    local pm_agent_session_id=$(get_pm_agent_session_id)
    if [ -z "$pm_agent_session_id" ] || [ "$pm_agent_session_id" = "null" ]; then
        echo "Error: No PM agent session. Run 'kugetsu init' first to create it." >&2
        exit 1
    fi

    local existing_session=$(get_session_for_issue "$issue_ref")
    if [ -n "$existing_session" ] && [ "$existing_session" != "null" ]; then
        echo "Error: Session for '$issue_ref' already exists" >&2
        echo "Use 'kugetsu continue $issue_ref <message>' instead" >&2
        exit 1
    fi

    local parent_dir="$PWD"
    local worktree_path=$(issue_ref_to_worktree_path "$issue_ref" "$parent_dir")
    create_worktree "$issue_ref" "$parent_dir"

    local session_file="$(issue_ref_to_filename "$issue_ref").json"

    echo "Forking session for '$issue_ref'..."
    
    # Session-counting: count actual dev sessions, reject if at limit
    local active_count=$(count_active_dev_sessions)
    if [ "$active_count" -ge "$MAX_CONCURRENT_AGENTS" ]; then
        echo "Error: Max concurrent agents ($MAX_CONCURRENT_AGENTS) reached" >&2
        echo "Active sessions: $active_count" >&2
        remove_worktree_for_issue "$issue_ref" "$parent_dir"
        exit 1
    fi
     
    local fork_log="$SESSIONS_DIR/$session_file.fork.log"
    local opencode_db="${OPENCODE_DB:-$HOME/.local/share/opencode/opencode.db}"
    
    > "$fork_log"
    
    local fork_context=$(kugetsu_get_fork_context "$issue_ref")
    local full_message="${fork_context}

## YOUR TASK
$message"
    
    fix_session_permissions
    
    if [ "$DEBUG_MODE" = true ]; then
        (cd "$worktree_path" && opencode run "$full_message" --fork --session "$base_session_id" --dir "$worktree_path" 2>&1) | tee "$fork_log" &
    else
        (cd "$worktree_path" && opencode run "$full_message" --fork --session "$base_session_id" --dir "$worktree_path" 2>&1) >> "$fork_log" &
    fi

    local fork_pid=$!
    
    local max_attempts=10
    local attempt=1
    local new_session_id=""
    local fork_log_output=""
    
    while [ $attempt -le $max_attempts ]; do
        sleep 1
        
        new_session_id=$(python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
cursor.execute(\"SELECT id FROM session WHERE directory = '$worktree_path' ORDER BY time_created DESC LIMIT 1\")
result = cursor.fetchone()
if result:
    print(result[0])
" 2>/dev/null || echo "")
        
        if [ -n "$new_session_id" ] && [ "$new_session_id" != "$base_session_id" ] && [ "$new_session_id" != "$pm_agent_session_id" ]; then
            break
        fi
        
        if ! kill -0 $fork_pid 2>/dev/null; then
            fork_log_output=$(tail -20 "$fork_log" 2>/dev/null || echo "(log empty or unavailable)")
            break
        fi
        
        attempt=$((attempt + 1))
    done

    if [ -z "$new_session_id" ]; then
        echo "Error: Could not find newly created session after ${max_attempts}s" >&2
        if [ -n "$fork_log_output" ]; then
            echo "Fork log output:" >&2
            echo "$fork_log_output" >&2
        fi
        remove_worktree_for_issue "$issue_ref"
        exit 1
    fi

    echo "Updating permissions for new session: $new_session_id"
    python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
PERMISSION_JSON = '[{\"permission\":\"question\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"plan_enter\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"plan_exit\",\"pattern\":\"*\",\"action\":\"deny\"},{\"permission\":\"external_directory\",\"pattern\":\"*\",\"action\":\"allow\"}]'
cursor.execute('UPDATE session SET permission = ? WHERE id = ?', (PERMISSION_JSON, '$new_session_id'))
conn.commit()
print('[OK] Session permissions updated')
"

    if [ "$DEBUG_MODE" = true ]; then
        echo "[DEBUG] Forked session permissions check:"
        python3 -c "
import sqlite3
conn = sqlite3.connect('$opencode_db')
cursor = conn.cursor()
cursor.execute(\"SELECT id, directory, permission FROM session WHERE id = '$new_session_id'\")
for row in cursor.fetchall():
    print('  ID:', row[0])
    print('  Directory:', row[1])
    print('  Permission:', row[2])
" 2>/dev/null || echo "  (failed to query DB)"
    fi

    local branch_name=$(issue_ref_to_branch_name "$issue_ref")
    
    python3 << PYEOF > "$SESSIONS_DIR/$session_file"
import json

session = {
    "type": "forked",
    "issue_ref": "$issue_ref",
    "opencode_session_id": "$new_session_id",
    "worktree_path": "$worktree_path",
    "created_at": "$(date -Iseconds)",
    "state": "idle",
    "branch_name": "$branch_name",
    "pr_url": "$pr_url" if "$pr_url" else None
}

with open("$SESSIONS_DIR/$session_file", "w") as f:
    json.dump(session, f, indent=2)
PYEOF

    add_issue_to_index "$issue_ref" "$session_file"

    echo "Session started for '$issue_ref': $new_session_id"
    echo "Worktree: $worktree_path"
}

cmd_continue() {
    local session_name=""
    local message=""
    local args=("$@")

    args=$(set_debug_mode "${args[@]}")

    for arg in $args; do
        if [ -z "$session_name" ]; then
            session_name="$arg"
        elif [ -z "$message" ]; then
            message="$arg"
        fi
    done

    if [ -z "$session_name" ]; then
        echo "Error: continue requires <session-name>" >&2
        exit 1
    fi

    if [ -z "$message" ]; then
        echo "Error: continue requires <message>" >&2
        exit 1
    fi

    local session_file=$(get_session_for_issue "$session_name")
    if [ -z "$session_file" ] || [ "$session_file" = "null" ]; then
        echo "Error: No session found for '$session_name'" >&2
        echo "Use 'kugetsu start <issue-ref> <message>' to create one" >&2
        exit 1
    fi

    local session_path="$SESSIONS_DIR/$session_file"
    if [ ! -f "$session_path" ]; then
        echo "Error: Session file missing: $session_path" >&2
        echo "Run 'kugetsu start <issue-ref> <message>' to recreate" >&2
        exit 1
    fi

    local opencode_session_id=$(python3 -c "import json; print(json.load(open('$session_path'))['opencode_session_id'])")
    local worktree_path=$(python3 -c "import json; print(json.load(open('$session_path')).get('worktree_path', ''))" 2>/dev/null || echo "")

    echo "Continuing session for '$session_name'..."
    # Note: --continue always allowed (existing sessions don't count toward limit)
    # Wrap in subshell with cd to ensure worktree directory is set correctly in session DB
    if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
        echo "Using worktree: $worktree_path"
        if [ "$DEBUG_MODE" = true ]; then
            (cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" --dir "$worktree_path" 2>&1) | tee "$session_path.debug.log" &
        else
            (cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" --dir "$worktree_path" 2>&1) &
        fi
    else
        if [ "$DEBUG_MODE" = true ]; then
            opencode run "$message" --continue --session "$opencode_session_id" 2>&1 | tee "$session_path.debug.log" &
        else
            opencode run "$message" --continue --session "$opencode_session_id" 2>&1 &
        fi
    fi
}

cmd_list() {
    ensure_dirs

    printf "%-50s %-10s %-25s %-40s\n" "ISSUE_REF" "TYPE" "SESSION_ID" "WORKTREE"
    printf "%-50s %-10s %-25s %-40s\n" "─────────" "─────" "──────────" "────────"

    local base_session_id=$(get_base_session_id)
    if [ -n "$base_session_id" ] && [ "$base_session_id" != "null" ]; then
        printf "%-50s %-10s %-25s %-40s\n" "(base)" "base" "$base_session_id" "N/A"
    fi

    local pm_agent_session_id=$(get_pm_agent_session_id)
    if [ -n "$pm_agent_session_id" ] && [ "$pm_agent_session_id" != "null" ]; then
        local pm_created="N/A"
        if [ -f "$SESSIONS_DIR/pm-agent.json" ]; then
            pm_created=$(python3 -c "import json; print(json.load(open('$SESSIONS_DIR/pm-agent.json'))['created_at'])" 2>/dev/null || echo "N/A")
        fi
        printf "%-50s %-10s %-25s %-40s\n" "(pm-agent)" "pm_agent" "$pm_agent_session_id" "N/A"
    fi

    local index=$(read_index)
    local issue_refs=$(echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); print('\n'.join(d['issues'].keys()))" 2>/dev/null || true)

    for session_file in "$SESSIONS_DIR"/*.json; do
        if [ -f "$session_file" ]; then
            local filename=$(basename "$session_file" .json)
            if [ "$filename" = "base" ] || [ "$filename" = "pm-agent" ]; then
                continue
            fi

            local issue_ref=$(python3 -c "import json; print(json.load(open('$session_file'))['issue_ref'])" 2>/dev/null || echo "$filename")
            local sess_id=$(python3 -c "import json; print(json.load(open('$session_file'))['opencode_session_id'])" 2>/dev/null || echo "unknown")
            local created=$(python3 -c "import json; print(json.load(open('$session_file'))['created_at'])" 2>/dev/null || echo "unknown")
            local worktree=$(python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', 'N/A'))" 2>/dev/null || echo "N/A")

            printf "%-50s %-10s %-25s %-40s\n" "$issue_ref" "forked" "$sess_id" "$worktree"
        fi
    done
}

cmd_prune() {
    local force=false

    while [ $# -gt 0 ]; do
        case "$1" in
            --force)
                force=true
                ;;
        esac
        shift
    done

    ensure_dirs
    ensure_worktree_dir

    local index=$(read_index)
    local index_session_files=$(echo "$index" | python3 -c "import sys, json; d=json.load(sys.stdin); sessions=set(d['issues'].values()); sessions.add('base.json'); sessions.add('pm-agent.json'); print('\n'.join(sessions))" 2>/dev/null || echo -e "base.json\npm-agent.json")

    local orphaned=()
    for session_file in "$SESSIONS_DIR"/*.json; do
        if [ -f "$session_file" ]; then
            local filename=$(basename "$session_file")
            if ! echo "$index_session_files" | grep -q "^$filename$"; then
                orphaned+=("$session_file")
            fi
        fi
    done

    local orphaned_worktrees=()
    if [ -d "$WORKTREES_DIR" ]; then
        for worktree_path in "$WORKTREES_DIR"/*; do
            if [ -d "$worktree_path" ]; then
                local worktree_name=$(basename "$worktree_path")
                local session_name="${worktree_name}.json"
                if ! echo "$index_session_files" | grep -q "^${session_name}$"; then
                    orphaned_worktrees+=("$worktree_path")
                fi
            fi
        done
    fi

    if [ ${#orphaned[@]} -eq 0 ] && [ ${#orphaned_worktrees[@]} -eq 0 ]; then
        echo "No orphaned sessions or worktrees found"
        return
    fi

    if [ ${#orphaned[@]} -gt 0 ]; then
        echo "Found ${#orphaned[@]} orphaned session(s):"
        for f in "${orphaned[@]}"; do
            echo "  - $(basename "$f")"
        done
    fi

    if [ ${#orphaned_worktrees[@]} -gt 0 ]; then
        echo "Found ${#orphaned_worktrees[@]} orphaned worktree(s):"
        for wt in "${orphaned_worktrees[@]}"; do
            echo "  - $(basename "$wt")"
        done
    fi

    if [ "$force" = true ]; then
        echo "Removing orphaned items (force mode)..."
        for f in "${orphaned[@]}"; do
            rm -f "$f"
            echo "Removed session: $(basename "$f")"
        done
        for wt in "${orphaned_worktrees[@]}"; do
            git worktree remove "$wt" 2>/dev/null || rm -rf "$wt"
            echo "Removed worktree: $(basename "$wt")"
        done
    else
        echo "Run with --force to remove"
    fi
}

cmd_destroy() {
    local target=""
    local force=false

    while [ $# -gt 0 ]; do
        case "$1" in
            --base)
                target="base"
                ;;
            --pm-agent)
                target="pm-agent"
                ;;
            -y|--yes)
                force=true
                ;;
            *)
                if [ -z "$target" ]; then
                    target="$1"
                fi
                ;;
        esac
        shift
    done

    if [ -z "$target" ]; then
        echo "Error: destroy requires <issue-ref>, --base, or --pm-agent" >&2
        exit 1
    fi

    if [ "$target" = "base" ]; then
        if [ "$force" = true ]; then
            local base_session_id=$(get_base_session_id)
            local pm_agent_session_id=$(get_pm_agent_session_id)
            rm -f "$SESSIONS_DIR/base.json"
            rm -f "$SESSIONS_DIR/pm-agent.json"
            rm -f "$SESSIONS_DIR/issue-"*.json 2>/dev/null || true
            echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE"
            
            if [ -n "$base_session_id" ] && [ "$base_session_id" != "null" ]; then
                echo "Deleting base session: $base_session_id"
                opencode session delete "$base_session_id" 2>/dev/null || echo "Warning: Could not delete base session"
            fi
            if [ -n "$pm_agent_session_id" ] && [ "$pm_agent_session_id" != "null" ]; then
                echo "Deleting PM agent session: $pm_agent_session_id"
                opencode session delete "$pm_agent_session_id" 2>/dev/null || echo "Warning: Could not delete PM agent session"
            fi
            echo "Base and PM agent sessions destroyed"
        else
            echo "Error: destroying base session requires --base -y" >&2
            exit 1
        fi
        return
    fi

    if [ "$target" = "pm-agent" ]; then
        if [ "$force" = true ]; then
            local pm_session_id=$(get_pm_agent_session_id)
            rm -f "$SESSIONS_DIR/pm-agent.json"
            local base=$(get_base_session_id)
            if [ -n "$base" ] && [ "$base" != "null" ]; then
                write_index "\"$base\"" "null" "{}"
            else
                write_index "null" "null" "{}"
            fi
            if [ -n "$pm_session_id" ] && [ "$pm_session_id" != "null" ]; then
                echo "Deleting opencode session: $pm_session_id"
                opencode session delete "$pm_session_id" 2>/dev/null || echo "Warning: Could not delete session from opencode (may already be deleted)"
            fi
            echo "PM agent session destroyed"
        else
            echo "Error: destroying pm-agent session requires --pm-agent -y" >&2
            exit 1
        fi
        return
    fi

    validate_issue_ref "$target"

    local session_file=$(get_session_for_issue "$target")
    if [ -z "$session_file" ] || [ "$session_file" = "null" ]; then
        echo "Error: No session found for '$target'" >&2
        exit 1
    fi

    local session_path="$SESSIONS_DIR/$session_file"

    if [ "$force" = true ]; then
        remove_worktree_for_issue "$target"
        rm -f "$session_path"
        remove_issue_from_index "$target"
        echo "Session for '$target' destroyed"
    else
        if [ "$WORKTREE_CHECK_PR_STATUS" = "true" ]; then
            local pr_url=$(python3 -c "import json; print(json.load(open('$session_path')).get('pr_url', '') or '')" 2>/dev/null || echo "")
            if [ -n "$pr_url" ] && [ "$pr_url" != "None" ]; then
                echo "Checking PR status at '$pr_url'..."
                local pr_status=$(check_pr_status "$pr_url")
                if [ "$pr_status" = "open" ]; then
                    echo "Error: PR is still open at $pr_url" >&2
                    echo "Use --force to destroy anyway, or close the PR first" >&2
                    exit 1
                elif [ "$pr_status" = "merged" ]; then
                    echo "PR has been merged. Safe to destroy."
                elif [ "$pr_status" = "closed" ]; then
                    echo "PR has been closed. Safe to destroy."
                else
                    echo "Warning: Could not determine PR status (got: $pr_status). Proceeding anyway." >&2
                fi
            fi
        fi
        
        echo "Delete session and worktree for '$target'? [y/N] "
        local reply
        read reply
        if [ "$reply" = "y" ] || [ "$reply" = "Y" ]; then
            remove_worktree_for_issue "$target"
            rm -f "$session_path"
            remove_issue_from_index "$target"
            echo "Session for '$target' destroyed"
        else
            echo "Aborted"
        fi
    fi
}

main() {
    if [ $# -eq 0 ]; then
        usage
        exit 1
    fi

    local command="$1"
    shift

    case "$command" in
        help|--help|-h)
            usage
            ;;
        init)
            cmd_init "$@"
            ;;
        start)
            cmd_start "$@"
            ;;
        continue)
            cmd_continue "$@"
            ;;
        delegate)
            cmd_delegate "$@"
            ;;
        logs)
            shift
            cmd_logs "$@"
            ;;
        status)
            cmd_status
            ;;
        server)
            cmd_server "$@"
            ;;
        env)
            cmd_env "$@"
            ;;
        doctor)
            cmd_doctor "$@"
            ;;
        notify)
            cmd_notify "$@"
            ;;
        list)
            cmd_list "$@"
            ;;
        prune)
            cmd_prune "$@"
            ;;
        destroy)
            cmd_destroy "$@"
            ;;
        set-pr)
            local issue_ref="${1:-}"
            local pr_url="${2:-}"
            if [ -z "$issue_ref" ] || [ -z "$pr_url" ]; then
                echo "Usage: kugetsu set-pr <issue-ref> <pr-url>" >&2
                echo "Example: kugetsu set-pr github.com/shoko/kugetsu#14 https://github.com/shoko/kugetsu/pulls/123" >&2
                exit 1
            fi
            validate_issue_ref "$issue_ref"
            update_session_pr_url "$issue_ref" "$pr_url"
            ;;
        *)
            echo "Error: unknown command '$command'" >&2
            usage
            exit 1
            ;;
    esac
}

main "$@"