#!/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"
MAX_CONCURRENT_AGENTS="${MAX_CONCURRENT_AGENTS:-5}"
AGENT_COUNT_FILE="$KUGETSU_DIR/.agent_count"
AGENT_LOCK_FILE="$KUGETSU_DIR/.agent_lock"

acquire_agent_slot() {
    local timeout="${1:-300}"
    local waited=0
    (
        flock -w 1 200 || { echo "Error: Could not acquire lock" >&2; exit 1; }
        local count
        count=$(cat "$AGENT_COUNT_FILE" 2>/dev/null || echo 0)
        if [ "$count" -lt "$MAX_CONCURRENT_AGENTS" ]; then
            echo $((count + 1)) > "$AGENT_COUNT_FILE"
            exit 0
        fi
        exit 1
    ) 200>"$AGENT_LOCK_FILE"
    local result=$?
    if [ $result -ne 0 ]; then
        local count
        count=$(cat "$AGENT_COUNT_FILE" 2>/dev/null || echo 0)
        if [ $waited -ge $timeout ]; then
            echo "Error: Timeout waiting for agent slot (max: $MAX_CONCURRENT_AGENTS, current: $count)" >&2
        fi
        return 1
    fi
    return 0
}

release_agent_slot() {
    (
        flock -w 1 200 || true
        local count
        count=$(cat "$AGENT_COUNT_FILE" 2>/dev/null || echo 0)
        if [ "$count" -gt 0 ]; then
            echo $((count - 1)) > "$AGENT_COUNT_FILE"
        fi
    ) 200>"$AGENT_LOCK_FILE"
}

run_with_limit() {
    local log_file="$1"
    shift
    local cmd=("$@")
    (
        "${cmd[@]}" >> "$log_file" 2>&1
        release_agent_slot
    ) &
    disown
}
>>>>>>> Stashed changes

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
    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 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.
                PM context is loaded once at init time.
    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 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 worktree_name=$(issue_ref_to_worktree_name "$issue_ref")
    echo "$WORKTREES_DIR/$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/#.*//')
    echo "https://${instance}/${rest}.git"
}

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

create_worktree() {
    local issue_ref="$1"
    local worktree_path=$(issue_ref_to_worktree_path "$issue_ref")
    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
    
    ensure_worktree_dir
    
    if worktree_exists "$issue_ref"; 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 --bare "$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 worktree_path=$(issue_ref_to_worktree_path "$issue_ref")
    
    if worktree_exists "$issue_ref"; 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
}

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'
}

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_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"
}

cmd_delegate() {
    local message="${1:-}"
    
    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
    
    opencode run --continue --session "$pm_session" "$message" 2>&1
}

cmd_doctor() {
    local fix=false
    
    while [ $# -gt 0 ]; do
        case "$1" in
            --fix)
                fix=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 --fork --session "$base" "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. $pm_context" 2>&1 || true
                    else
                        opencode run --fork --session "$base" "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions." 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
}

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_init() {
    local force=false

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

    ensure_dirs

    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

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

    opencode

    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
    
    opencode run --fork --session "$new_session_id" "$pm_prompt" 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}"
}

cmd_start() {
    local issue_ref=""
    local message=""
    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"
        fi
    done

    if [ -z "$issue_ref" ] || [ -z "$message" ]; then
        echo "Error: start requires <issue-ref> and <message>" >&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 worktree_path=$(issue_ref_to_worktree_path "$issue_ref")
    create_worktree "$issue_ref"

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

    local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
    local before_set="${before_sessions//$'\n'/|}"

    echo "Forking session for '$issue_ref'..."
    if [ "$DEBUG_MODE" = true ]; then
        opencode run --fork --session "$base_session_id" "$message" --workdir "$worktree_path" 2>&1 | tee "$SESSIONS_DIR/$session_file.debug.log"
    else
        opencode run --fork --session "$base_session_id" "$message" --workdir "$worktree_path" 2>&1
    fi

    local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
    local new_session_id=""
    while IFS= read -r sess; do
        if [[ ! "$before_set" =~ \|${sess}\| ]] && [[ "$sess" != "$base_session_id" ]]; then
            new_session_id="$sess"
            break
        fi
    done <<< "$after_sessions"

    if [ -z "$new_session_id" ]; then
        echo "Error: Could not find newly created session" >&2
        remove_worktree_for_issue "$issue_ref"
        exit 1
    fi

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

    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'..."
    if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
        echo "Using worktree: $worktree_path"
        if [ "$DEBUG_MODE" = true ]; then
            opencode run --continue --session "$opencode_session_id" "$message" --workdir "$worktree_path" 2>&1 | tee "$session_path.debug.log"
        else
            opencode run --continue --session "$opencode_session_id" "$message" --workdir "$worktree_path"
        fi
    else
        if [ "$DEBUG_MODE" = true ]; then
            opencode run --continue --session "$opencode_session_id" "$message" 2>&1 | tee "$session_path.debug.log"
        else
            opencode run --continue --session "$opencode_session_id" "$message"
        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
            rm -f "$SESSIONS_DIR/base.json"
            local pm_agent=$(get_pm_agent_session_id)
            if [ -n "$pm_agent" ] && [ "$pm_agent" != "null" ]; then
                rm -f "$SESSIONS_DIR/pm-agent.json"
                echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE"
            else
                echo '{"base": null, "pm_agent": null, "issues": {}}' > "$INDEX_FILE"
            fi
            echo "Base session 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
            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
            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
        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 "$@"
            ;;
        status)
            cmd_status
            ;;
        doctor)
            cmd_doctor "$@"
            ;;
        notify)
            cmd_notify "$@"
            ;;
        list)
            cmd_list "$@"
            ;;
        prune)
            cmd_prune "$@"
            ;;
        destroy)
            cmd_destroy "$@"
            ;;
        *)
            echo "Error: unknown command '$command'" >&2
            usage
            exit 1
            ;;
    esac
}

main "$@"