diff --git a/skills/kugetsu/scripts/kugetsu-config.sh b/skills/kugetsu/scripts/kugetsu-config.sh index 3a033c0..3d8c56e 100755 --- a/skills/kugetsu/scripts/kugetsu-config.sh +++ b/skills/kugetsu/scripts/kugetsu-config.sh @@ -26,6 +26,9 @@ QUEUE_DAEMON_INTERVAL_MINUTES="${QUEUE_DAEMON_INTERVAL_MINUTES:-5}" QUEUE_CLEANUP_AGE_DAYS="${QUEUE_CLEANUP_AGE_DAYS:-7}" TASK_TIMEOUT_HOURS="${TASK_TIMEOUT_HOURS:-1}" +NETWORK_RETRY_ATTEMPTS="${NETWORK_RETRY_ATTEMPTS:-3}" +NETWORK_RETRY_DELAY_SECONDS="${NETWORK_RETRY_DELAY_SECONDS:-5}" + # Load user config overrides (~/.kugetsu/config) if [ -f "$KUGETSU_DIR/config" ]; then source "$KUGETSU_DIR/config" @@ -87,3 +90,24 @@ set_debug_mode() { echo "${filtered_args[@]}" } + +retry_with_backoff() { + local max_attempts="${1:-$NETWORK_RETRY_ATTEMPTS}" + local delay_seconds="${2:-$NETWORK_RETRY_DELAY_SECONDS}" + local command="$3" + local remaining_attempts=$max_attempts + + while [ $remaining_attempts -gt 0 ]; do + if eval "$command"; then + return 0 + fi + remaining_attempts=$((remaining_attempts - 1)) + if [ $remaining_attempts -gt 0 ]; then + log "warn" "retry_with_backoff" "Command failed, $remaining_attempts retries remaining. Waiting ${delay_seconds}s..." + sleep "$delay_seconds" + delay_seconds=$((delay_seconds * 2)) + fi + done + log "error" "retry_with_backoff" "Command failed after $max_attempts attempts" + return 1 +} diff --git a/skills/kugetsu/scripts/kugetsu-session.sh b/skills/kugetsu/scripts/kugetsu-session.sh index f9d6f19..f81a3a3 100755 --- a/skills/kugetsu/scripts/kugetsu-session.sh +++ b/skills/kugetsu/scripts/kugetsu-session.sh @@ -13,7 +13,8 @@ count_active_dev_sessions() { if [ -d "$SESSIONS_DIR" ]; then for session_file in "$SESSIONS_DIR"/*.json; do if [ -f "$session_file" ]; then - local filename=$(basename "$session_file") + local filename + filename=$(basename "$session_file") if [ "$filename" != "base.json" ] && [ "$filename" != "pm-agent.json" ]; then count=$((count + 1)) fi @@ -239,18 +240,39 @@ create_session() { return 1 fi - local before_file="$KUGETSU_DIR/sessions/before$$.json" - local after_file="$KUGETSU_DIR/sessions/after$$.json" + local before_file + before_file="$KUGETSU_DIR/sessions/before$$.json" + local after_file + after_file="$KUGETSU_DIR/sessions/after$$.json" opencode session list --format=json > "$before_file" 2>/dev/null || printf '{}' > "$before_file" - opencode run --fork --session "$base_session" "new session" >/dev/null 2>&1 + local fork_success=false + local attempt=0 + local max_attempts="${NETWORK_RETRY_ATTEMPTS:-3}" + + while [ $attempt -lt $max_attempts ] && [ "$fork_success" = false ]; do + attempt=$((attempt + 1)) + if opencode run --fork --session "$base_session" "new session" >/dev/null 2>&1; then + fork_success=true + elif [ $attempt -lt $max_attempts ]; then + log "warn" "create_session" "Fork attempt $attempt failed, retrying..." + sleep "$((attempt * 2))" + fi + done + + if [ "$fork_success" = false ]; then + log "error" "create_session" "Failed to fork session after $max_attempts attempts" + rm -f "$before_file" "$after_file" + return 1 + fi sleep 1 opencode session list --format=json > "$after_file" 2>/dev/null || printf '{}' > "$after_file" - local new_session_id=$(python3 << PYEOF + local new_session_id + new_session_id=$(python3 << PYEOF import json with open("$before_file", 'r') as f: @@ -389,20 +411,20 @@ ensure_session() { session_exists=true fi - if $worktree_exists && $session_exists; then + if [ "$worktree_exists" = true ] && [ "$session_exists" = true ]; then log "info" "ensure_session" "Session already exists for $issue_ref" echo "continued" return 0 fi - if ! $worktree_exists && $session_exists; then + if [ "$worktree_exists" = false ] && [ "$session_exists" = true ]; then log "warn" "ensure_session" "Session exists but worktree is missing. Removing stale session..." rm -f "$session_path" remove_issue_from_index "$issue_ref" session_exists=false fi - if ! $worktree_exists; then + if [ "$worktree_exists" = false ]; then local wt_status=$(ensure_worktree "$issue_ref") if [ "$wt_status" != "created" ] && [ "$wt_status" != "existed" ]; then log "error" "ensure_session" "Failed to ensure worktree for $issue_ref" diff --git a/skills/kugetsu/scripts/kugetsu-worktree.sh b/skills/kugetsu/scripts/kugetsu-worktree.sh index a08ccee..cfa0e42 100755 --- a/skills/kugetsu/scripts/kugetsu-worktree.sh +++ b/skills/kugetsu/scripts/kugetsu-worktree.sh @@ -76,7 +76,8 @@ create_worktree() { exit 1 fi - local worktree_parent_dir=$(dirname "$worktree_path") + local worktree_parent_dir + worktree_parent_dir=$(dirname "$worktree_path") mkdir -p "$worktree_parent_dir" if worktree_exists "$issue_ref" "$parent_dir"; then @@ -85,15 +86,36 @@ create_worktree() { fi echo "Creating worktree at '$worktree_path'..." - git clone "$repo_url" "$worktree_path" 2>/dev/null || { - echo "Error: Failed to clone repository" >&2 + + local clone_success=false + local attempt=0 + local max_attempts="${NETWORK_RETRY_ATTEMPTS:-3}" + + while [ $attempt -lt $max_attempts ] && [ "$clone_success" = false ]; do + attempt=$((attempt + 1)) + if [ $attempt -gt 1 ]; then + echo "Clone attempt $attempt of $max_attempts..." + sleep "$((attempt * 2))" + fi + + if git clone "$repo_url" "$worktree_path" 2>/dev/null; then + clone_success=true + fi + done + + if [ "$clone_success" = false ]; then + echo "Error: Failed to clone repository after $max_attempts attempts" >&2 exit 1 - } + fi 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) || { + if git -C "$worktree_path" checkout -b "$branch_name" origin/main 2>/dev/null; then + : + elif git -C "$worktree_path" checkout -b "$branch_name" main 2>/dev/null; then + : + else echo "Warning: Could not checkout branch (may need to run from within worktree after session)" >&2 - } + fi echo "Worktree created at: $worktree_path" }