From d62ecb884ebbbbc1314a492e6c17ab043c49b8a3 Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Thu, 2 Apr 2026 03:18:36 +0000 Subject: [PATCH] feat(kugetsu): add lock mechanism for worktree coordination --- skills/kugetsu/scripts/kugetsu | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/skills/kugetsu/scripts/kugetsu b/skills/kugetsu/scripts/kugetsu index e96b43f..e72384e 100755 --- a/skills/kugetsu/scripts/kugetsu +++ b/skills/kugetsu/scripts/kugetsu @@ -10,6 +10,7 @@ NOTIFICATIONS_FILE="$KUGETSU_DIR/notifications.json" LOGS_DIR="$KUGETSU_DIR/logs" ENV_DIR="${ENV_DIR:-$KUGETSU_DIR/env}" QUEUE_FILE="$KUGETSU_DIR/queue.json" +LOCKS_DIR="$KUGETSU_DIR/locks" MAX_CONCURRENT_AGENTS="${MAX_CONCURRENT_AGENTS:-3}" KUGETSU_VERBOSITY="${KUGETSU_VERBOSITY:-total}" POLL_INTERVAL="${POLL_INTERVAL:-600}" @@ -59,6 +60,87 @@ count_active_dev_sessions() { echo "$count" } +acquire_lock() { + local issue_ref="$1" + local session_id="$2" + local lock_file="$LOCKS_DIR/${issue_ref//[^a-zA-Z0-9._-]/_}.lock" + + mkdir -p "$LOCKS_DIR" + + if [ -f "$lock_file" ]; then + local lock_pid=$(cat "$lock_file" 2>/dev/null | cut -d: -f1) + local lock_session=$(cat "$lock_file" 2>/dev/null | cut -d: -f2) + + if [ -n "$lock_pid" ] && ! kill -0 "$lock_pid" 2>/dev/null; then + echo "Stale lock detected, removing..." + rm -f "$lock_file" + elif [ "$lock_session" = "$session_id" ]; then + echo "Already holding lock for $issue_ref" + return 0 + else + echo "Error: $issue_ref is locked by session $lock_session (PID $lock_pid)" >&2 + return 1 + fi + fi + + echo "${BASHPID}:${session_id}:$(date +%s)" > "$lock_file" + echo "Lock acquired for $issue_ref: $lock_file" + return 0 +} + +release_lock() { + local issue_ref="$1" + local lock_file="$LOCKS_DIR/${issue_ref//[^a-zA-Z0-9._-]/_}.lock" + + if [ ! -f "$lock_file" ]; then + return 0 + fi + + local lock_pid=$(cat "$lock_file" 2>/dev/null | cut -d: -f1) + + if [ "$lock_pid" = "$BASHPID" ]; then + rm -f "$lock_file" + echo "Lock released for $issue_ref" + else + echo "Error: Cannot release lock held by PID $lock_pid (current: $BASHPID)" >&2 + return 1 + fi +} + +release_all_locks() { + local session_id="$1" + if [ ! -d "$LOCKS_DIR" ]; then + return 0 + fi + for lock_file in "$LOCKS_DIR"/*.lock; do + [ -f "$lock_file" ] || continue + local lock_session=$(cat "$lock_file" 2>/dev/null | cut -d: -f2) + if [ "$lock_session" = "$session_id" ]; then + rm -f "$lock_file" + echo "Released stale lock: $(basename "$lock_file")" + fi + done +} + +check_lock() { + local issue_ref="$1" + local lock_file="$LOCKS_DIR/${issue_ref//[^a-zA-Z0-9._-]/_}.lock" + + if [ -f "$lock_file" ]; then + local lock_pid=$(cat "$lock_file" 2>/dev/null | cut -d: -f1) + local lock_session=$(cat "$lock_file" 2>/dev/null | cut -d: -f2) + + if [ -n "$lock_pid" ] && ! kill -0 "$lock_pid" 2>/dev/null; then + echo "Stale lock detected, removing..." + rm -f "$lock_file" + return 1 + fi + echo "Locked by session $lock_session (PID $lock_pid)" + return 0 + fi + return 1 +} + usage() { cat << 'EOF' kugetsu - OpenCode Session Manager (Issue-Driven) @@ -1266,6 +1348,14 @@ cmd_start() { fi local worktree_path=$(issue_ref_to_worktree_path "$issue_ref") + + trap 'release_lock "$issue_ref" 2>/dev/null; exit' EXIT INT TERM + + if ! acquire_lock "$issue_ref" "$base_session_id"; then + echo "Error: Could not acquire lock for '$issue_ref'" >&2 + exit 1 + fi + create_worktree "$issue_ref" local session_file="$(issue_ref_to_filename "$issue_ref").json" @@ -1332,6 +1422,8 @@ cmd_start() { echo "Session started for '$issue_ref': $new_session_id" echo "Worktree: $worktree_path" + + release_lock "$issue_ref" } cmd_continue() { @@ -1585,6 +1677,7 @@ cmd_destroy() { if [ "$force" = true ]; then remove_worktree_for_issue "$target" + release_lock "$target" 2>/dev/null || true rm -f "$session_path" remove_issue_from_index "$target" echo "Session for '$target' destroyed" @@ -1594,6 +1687,7 @@ cmd_destroy() { read reply if [ "$reply" = "y" ] || [ "$reply" = "Y" ]; then remove_worktree_for_issue "$target" + release_lock "$target" 2>/dev/null || true rm -f "$session_path" remove_issue_from_index "$target" echo "Session for '$target' destroyed"