The init script used 'tail -1' to find newly created sessions, which fails when old sessions exist because it picks an existing session instead of the newly created one. The fix captures session IDs before and after creating new sessions, then diffs to identify newly created sessions. Fixes #172
601 lines
19 KiB
Bash
Executable File
601 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Source required modules for session management functions
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/kugetsu-config.sh"
|
|
source "$SCRIPT_DIR/kugetsu-index.sh"
|
|
source "$SCRIPT_DIR/kugetsu-worktree.sh"
|
|
source "$SCRIPT_DIR/kugetsu-log.sh"
|
|
|
|
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" ] && [ "$filename" != "pm-agent.json" ]; then
|
|
count=$((count + 1))
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
echo "$count"
|
|
}
|
|
|
|
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" ]; 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
|
|
|
|
# Git server configurations
|
|
# Format: GIT_SERVERS["hostname"]="https://hostname"
|
|
declare -A GIT_SERVERS
|
|
GIT_SERVERS["github.com"]="https://github.com"
|
|
GIT_SERVERS["git.fbrns.co"]="https://git.fbrns.co"
|
|
DEFAULT_GIT_SERVER="github.com"
|
|
EOF
|
|
echo "Created config file: $KUGETSU_DIR/config"
|
|
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
|
|
|
|
echo "Starting TUI to create base session..."
|
|
echo "Press Ctrl+C to cancel or wait for session to be created"
|
|
sleep 2
|
|
|
|
local before_sessions=$(opencode session list 2>/dev/null | grep -E '^ses_' | awk '{print $1}' || true)
|
|
|
|
opencode
|
|
|
|
local after_sessions=$(opencode session list 2>/dev/null | grep -E '^ses_' | awk '{print $1}' || true)
|
|
local session_ids=""
|
|
while IFS= read -r line; do
|
|
local sid=$(echo "$line" | awk '{print $1}')
|
|
if [ -n "$sid" ] && ! echo "$before_sessions" | grep -q "^${sid}$"; then
|
|
session_ids="$sid"
|
|
break
|
|
fi
|
|
done <<< "$after_sessions"
|
|
|
|
if [ -z "$session_ids" ]; then
|
|
echo "Error: Could not find newly created session" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "$session_ids" > "$SESSIONS_DIR/base.json"
|
|
set_base_in_index "$session_ids"
|
|
|
|
echo "Base session created: $session_ids"
|
|
echo "Starting PM agent..."
|
|
|
|
before_sessions="$after_sessions"
|
|
|
|
opencode
|
|
|
|
after_sessions=$(opencode session list 2>/dev/null | grep -E '^ses_' | awk '{print $1}' || true)
|
|
local pm_session_ids=""
|
|
while IFS= read -r line; do
|
|
local sid=$(echo "$line" | awk '{print $1}')
|
|
if [ -n "$sid" ] && ! echo "$before_sessions" | grep -q "^${sid}$"; then
|
|
pm_session_ids="$sid"
|
|
break
|
|
fi
|
|
done <<< "$after_sessions"
|
|
|
|
if [ -z "$pm_session_ids" ]; then
|
|
echo "Warning: Could not find separate PM agent session" >&2
|
|
pm_session_ids="$session_ids"
|
|
fi
|
|
|
|
echo "$pm_session_ids" > "$SESSIONS_DIR/pm-agent.json"
|
|
set_pm_agent_in_index "$pm_session_ids"
|
|
|
|
load_agent_env "pm-agent"
|
|
|
|
local pm_system_prompt=""
|
|
if [ -f "$KUGETSU_DIR/pm-agent.md" ]; then
|
|
pm_system_prompt=$(cat "$KUGETSU_DIR/pm-agent.md")
|
|
echo "Injecting PM agent system prompt from $KUGETSU_DIR/pm-agent.md"
|
|
fi
|
|
|
|
echo "PM agent session created: $pm_session_ids"
|
|
echo ""
|
|
echo "kugetsu initialized successfully!"
|
|
echo " Base session: $session_ids"
|
|
echo " PM agent: $pm_session_ids"
|
|
}
|
|
|
|
extract_issue_ref_from_message() {
|
|
local message="$1"
|
|
|
|
if [ -z "$message" ]; then
|
|
echo ""
|
|
return
|
|
fi
|
|
|
|
if [[ "$message" =~ ^([a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+#[0-9]+) ]]; then
|
|
echo "${BASH_REMATCH[1]}"
|
|
return
|
|
fi
|
|
|
|
if [[ "$message" =~ (https?://[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+/(issues|pull)/[0-9]+) ]]; then
|
|
local url="${BASH_REMATCH[1]}"
|
|
local path=$(echo "$url" | sed 's|https\?://||' | cut -d'/' -f2-)
|
|
local instance=$(echo "$path" | cut -d'/' -f1)
|
|
local owner=$(echo "$path" | cut -d'/' -f2)
|
|
local repo=$(echo "$path" | cut -d'/' -f3)
|
|
local num=$(echo "$path" | grep -oE '[0-9]+$')
|
|
echo "${instance}/${owner}/${repo}#${num}"
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
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 issue_ref=$(extract_issue_ref_from_message "$message")
|
|
|
|
if [ -n "$issue_ref" ] && [[ "$issue_ref" =~ \#[0-9]+$ ]]; then
|
|
# Enqueue for daemon to process via cmd_start/cmd_continue
|
|
enqueue_task "$issue_ref" "$message"
|
|
return
|
|
fi
|
|
|
|
# No issue ref detected — delegate directly to PM agent (legacy path)
|
|
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"
|
|
nohup sh -c "GITEA_TOKEN='***' opencode run '$message' --continue --session '$pm_session' >> '$log_file' 2>&1" > /dev/null 2>&1 &
|
|
disown
|
|
echo "Delegated to PM agent (logged to $(basename "$log_file"))"
|
|
}
|
|
|
|
cmd_start() {
|
|
local issue_ref="${1:-}"
|
|
local message="${2:-}"
|
|
|
|
if [ -z "$issue_ref" ]; then
|
|
echo "Error: issue ref is required" >&2
|
|
echo "Usage: kugetsu start <issue-ref> [message]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
validate_issue_ref "$issue_ref"
|
|
|
|
local base_session_id=$(get_base_session_id)
|
|
if [ -z "$base_session_id" ] || [ "$base_session_id" = "null" ]; then
|
|
echo "Error: Base session not found. 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: PM agent session not found. Run 'kugetsu init' first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if worktree_exists "$issue_ref"; then
|
|
echo "Issue '$issue_ref' already has a worktree. Use 'kugetsu continue' instead."
|
|
exit 1
|
|
fi
|
|
|
|
local active_count=$(count_active_dev_sessions)
|
|
if [ "$active_count" -ge "${MAX_CONCURRENT_AGENTS:-3}" ]; then
|
|
echo "Error: Max concurrent agents (${MAX_CONCURRENT_AGENTS:-3}) reached. Use 'kugetsu continue' or wait for an agent to finish." >&2
|
|
exit 1
|
|
fi
|
|
|
|
local session_file=$(issue_ref_to_filename "$issue_ref")
|
|
local session_path="$SESSIONS_DIR/$session_file"
|
|
|
|
if [ -f "$session_path" ]; then
|
|
echo "Session file already exists: $session_file"
|
|
echo "Use 'kugetsu continue $issue_ref' to continue work."
|
|
exit 1
|
|
fi
|
|
|
|
local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
|
|
local before_set="|$before_sessions|"
|
|
|
|
create_worktree "$issue_ref"
|
|
|
|
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" ]] && [[ "$sess" != "$pm_agent_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
|
|
|
|
local worktree_path=$(issue_ref_to_worktree_path "$issue_ref")
|
|
|
|
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"
|
|
else
|
|
message="$arg"
|
|
fi
|
|
done
|
|
|
|
if [ -z "$session_name" ]; then
|
|
echo "Error: issue ref is required" >&2
|
|
echo "Usage: kugetsu continue <issue-ref> [message]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
validate_issue_ref "$session_name"
|
|
|
|
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 $session_name' to create a new session." >&2
|
|
exit 1
|
|
fi
|
|
|
|
local session_path="$SESSIONS_DIR/$session_file"
|
|
|
|
if [ ! -f "$session_path" ]; then
|
|
echo "Error: Session file not found: $session_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
load_agent_env "dev"
|
|
|
|
local opencode_session_id=$(python3 -c "import json; print(json.load(open('$session_path')).get('opencode_session_id', ''))" 2>/dev/null || echo "")
|
|
local worktree_path=$(python3 -c "import json; print(json.load(open('$session_path')).get('worktree_path', ''))" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
|
|
if [ -n "$message" ]; then
|
|
(cd "$worktree_path" && opencode run "$message" --continue --session "$opencode_session_id" "$@")
|
|
else
|
|
(cd "$worktree_path" && opencode --continue --session "$opencode_session_id" "$@")
|
|
fi
|
|
else
|
|
if [ -n "$message" ]; then
|
|
opencode run "$message" --continue --session "$opencode_session_id" "$@"
|
|
else
|
|
opencode --continue --session "$opencode_session_id" "$@"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
cmd_list() {
|
|
echo "=== kugetsu sessions ==="
|
|
echo ""
|
|
|
|
local base_id=$(get_base_session_id)
|
|
if [ -n "$base_id" ] && [ "$base_id" != "null" ]; then
|
|
echo "Base session: $base_id"
|
|
else
|
|
echo "Base session: not initialized"
|
|
fi
|
|
|
|
local pm_id=$(get_pm_agent_session_id)
|
|
if [ -n "$pm_id" ] && [ "$pm_id" != "null" ]; then
|
|
echo "PM agent: $pm_id"
|
|
else
|
|
echo "PM agent: not initialized"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Issue sessions:"
|
|
|
|
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" ] && [ "$filename" != "pm-agent.json" ]; then
|
|
local issue_ref=$(filename_to_issue_ref "$filename")
|
|
local opencode_sid=$(python3 -c "import json; print(json.load(open('$session_file')).get('opencode_session_id', 'unknown'))" 2>/dev/null || echo "unknown")
|
|
local state=$(python3 -c "import json; print(json.load(open('$session_file')).get('state', 'unknown'))" 2>/dev/null || echo "unknown")
|
|
local worktree_path=$(python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', ''))" 2>/dev/null || echo "")
|
|
local worktree_status=""
|
|
if [ -n "$worktree_path" ]; then
|
|
if [ -d "$worktree_path" ]; then
|
|
worktree_status="(worktree exists)"
|
|
else
|
|
worktree_status="(worktree MISSING)"
|
|
fi
|
|
fi
|
|
echo " $filename"
|
|
echo " Issue: $issue_ref"
|
|
echo " Session: $opencode_sid"
|
|
echo " State: $state"
|
|
echo " $worktree_status"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -d "$WORKTREES_DIR" ]; then
|
|
echo ""
|
|
echo "Worktrees without sessions:"
|
|
for worktree in $(find "$WORKTREES_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null); do
|
|
local worktree_name=$(basename "$worktree")
|
|
local has_session=false
|
|
for session_file in "$SESSIONS_DIR"/*.json; do
|
|
if [ -f "$session_file" ]; then
|
|
local wt_path=$(python3 -c "import json; print(json.load(open('$session_file')).get('worktree_path', ''))" 2>/dev/null || echo "")
|
|
if [ "$wt_path" = "$worktree" ]; then
|
|
has_session=true
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
if [ "$has_session" = false ]; then
|
|
echo " $worktree_name (no session)"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
cmd_prune() {
|
|
local force=false
|
|
|
|
if [ "$1" = "--force" ]; then
|
|
force=true
|
|
fi
|
|
|
|
echo "=== kugetsu prune ==="
|
|
echo ""
|
|
|
|
local orphaned=()
|
|
|
|
if [ -d "$SESSIONS_DIR" ]; then
|
|
for session_file in "$SESSIONS_DIR"/*.json; do
|
|
[ -f "$session_file" ] || continue
|
|
local filename=$(basename "$session_file")
|
|
if [ "$filename" = "base.json" ] || [ "$filename" = "pm-agent.json" ]; then
|
|
continue
|
|
fi
|
|
|
|
local opencode_sid=$(python3 -c "import json; print(json.load(open('$session_file')).get('opencode_session_id', ''))" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$opencode_sid" ]; then
|
|
local exists=$(opencode session list 2>/dev/null | grep -c "^$opencode_sid" || echo "0")
|
|
if [ "$exists" -eq 0 ]; then
|
|
orphaned+=("$session_file")
|
|
fi
|
|
else
|
|
orphaned+=("$session_file")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ ${#orphaned[@]} -eq 0 ]; then
|
|
echo "No orphaned sessions found."
|
|
return
|
|
fi
|
|
|
|
echo "Found ${#orphaned[@]} orphaned session(s):"
|
|
for session in "${orphaned[@]}"; do
|
|
echo " $session"
|
|
done
|
|
echo ""
|
|
|
|
if [ "$force" = false ]; then
|
|
read -p "Remove these sessions? [y/N] " -r answer
|
|
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
for session in "${orphaned[@]}"; do
|
|
local issue_ref=$(python3 -c "import json; print(json.load(open('$session')).get('issue_ref', ''))" 2>/dev/null || echo "")
|
|
local worktree_path=$(python3 -c "import json; print(json.load(open('$session')).get('worktree_path', ''))" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
|
|
echo "Removing worktree: $worktree_path"
|
|
git worktree remove "$worktree_path" 2>/dev/null || rm -rf "$worktree_path"
|
|
fi
|
|
|
|
rm -f "$session"
|
|
|
|
if [ -n "$issue_ref" ]; then
|
|
remove_issue_from_index "$issue_ref"
|
|
fi
|
|
|
|
echo "Removed: $session"
|
|
done
|
|
|
|
echo ""
|
|
echo "Pruned ${#orphaned[@]} orphaned session(s)."
|
|
}
|
|
|
|
cmd_destroy() {
|
|
local target="${1:-}"
|
|
local force=false
|
|
|
|
if [ "$target" = "--base" ]; then
|
|
target=""
|
|
fi
|
|
|
|
if [ "$2" = "-y" ]; then
|
|
force=true
|
|
fi
|
|
|
|
if [ -z "$target" ]; then
|
|
echo "Error: target is required" >&2
|
|
echo "Usage: kugetsu destroy <issue-ref> [-y]" >&2
|
|
echo " kugetsu destroy --pm-agent [-y]" >&2
|
|
echo " kugetsu destroy --base [-y]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$target" = "--pm-agent" ]; then
|
|
if [ "$force" = false ]; then
|
|
echo "Warning: Destroying PM agent session is not recommended." >&2
|
|
read -p "Continue? [y/N] " -r answer
|
|
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
local pm_session=$(get_pm_agent_session_id)
|
|
if [ -n "$pm_session" ] && [ "$pm_session" != "null" ]; then
|
|
echo "Stopping PM agent session: $pm_session"
|
|
opencode session stop "$pm_session" 2>/dev/null || true
|
|
fi
|
|
|
|
rm -f "$SESSIONS_DIR/pm-agent.json"
|
|
set_pm_agent_in_index "null"
|
|
echo "PM agent session destroyed"
|
|
elif [ "$target" = "--base" ]; then
|
|
if [ "$force" = false ]; then
|
|
echo "Warning: Destroying base session will remove ALL sessions." >&2
|
|
read -p "Continue? [y/N] " -r answer
|
|
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
for session_file in "$SESSIONS_DIR"/*.json; do
|
|
[ -f "$session_file" ] || continue
|
|
rm -f "$session_file"
|
|
done
|
|
|
|
for worktree in "$WORKTREES_DIR"/.kugetsu-worktrees/*; do
|
|
if [ -d "$worktree" ]; then
|
|
git worktree remove "$worktree" 2>/dev/null || rm -rf "$worktree"
|
|
fi
|
|
done
|
|
|
|
write_index "null" "null" "{}"
|
|
echo "Base session and all worktrees destroyed"
|
|
else
|
|
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 "Warning: This will delete session and worktree for '$target'" >&2
|
|
read -p "Continue? [y/N] " -r answer
|
|
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted."
|
|
return
|
|
fi
|
|
|
|
remove_worktree_for_issue "$target"
|
|
rm -f "$session_path"
|
|
remove_issue_from_index "$target"
|
|
echo "Session for '$target' destroyed"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
cmd_status() {
|
|
echo "=== kugetsu status ==="
|
|
|
|
local base_id=$(get_base_session_id)
|
|
local pm_id=$(get_pm_agent_session_id)
|
|
|
|
if [ -z "$base_id" ] || [ "$base_id" = "null" ]; then
|
|
echo "Status: Not initialized"
|
|
echo "Run 'kugetsu init' to initialize."
|
|
return
|
|
fi
|
|
|
|
echo "Base session: $base_id"
|
|
|
|
if [ -n "$pm_id" ] && [ "$pm_id" != "null" ]; then
|
|
echo "PM agent: $pm_id"
|
|
else
|
|
echo "PM agent: not running"
|
|
fi
|
|
|
|
local active_count=$(count_active_dev_sessions)
|
|
echo "Active issue sessions: $active_count / ${MAX_CONCURRENT_AGENTS:-3}"
|
|
|
|
echo ""
|
|
echo "OpenCode sessions:"
|
|
opencode session list 2>/dev/null || echo " (unable to list sessions)"
|
|
}
|