Skip to content

Commit 7d98bf5

Browse files
authored
Merge pull request #10 from kingstinct/improve-eternity-loop
Improve eternity-loop reliability and env handling
2 parents 924b03e + 587fafa commit 7d98bf5

2 files changed

Lines changed: 72 additions & 25 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.DS_Store
2+
.env.local

scripts/eternity-loop.sh

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,71 @@
1616
set -euo pipefail
1717

1818
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19-
TMUX_SESSION="eternity-loop"
19+
PROJECT_NAME="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")"
20+
TMUX_SESSION="eternity-loop-${PROJECT_NAME}"
2021
WORKTREE_NAME="eternity-loop"
2122

2223
# --- Worktree + tmux bootstrap ---
2324
# If not already inside the eternity-loop tmux session, set up worktree and launch
2425
if [ -z "${ETERNITY_LOOP_INSIDE:-}" ]; then
2526
REPO_ROOT="$(git rev-parse --show-toplevel)"
26-
WORKTREE_PATH="$REPO_ROOT/../$WORKTREE_NAME"
27+
WORKTREE_PATH="$REPO_ROOT/.claude/worktrees/$WORKTREE_NAME"
28+
29+
# Load .env in bootstrap phase so LINEAR_API_KEY can be passed to tmux
30+
for _env_candidate in \
31+
"$REPO_ROOT/.env.local" \
32+
"$REPO_ROOT/.env" \
33+
"$SCRIPT_DIR/.env.local" \
34+
"$SCRIPT_DIR/.env"; do
35+
if [ -f "$_env_candidate" ]; then
36+
set -a; source "$_env_candidate"; set +a
37+
break
38+
fi
39+
done
2740

2841
# Determine the main branch name
2942
MAIN_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@') || true
3043
[ -z "$MAIN_BRANCH" ] && MAIN_BRANCH="main"
3144

32-
# Create worktree if it doesn't exist (start on main branch)
33-
if [ ! -d "$WORKTREE_PATH" ]; then
34-
echo "Creating git worktree at $WORKTREE_PATH (branch: $MAIN_BRANCH)..."
35-
git worktree add "$WORKTREE_PATH" --detach "origin/$MAIN_BRANCH"
36-
else
37-
echo "Worktree already exists at $WORKTREE_PATH"
38-
# Ensure worktree is on main and up to date
39-
cd "$WORKTREE_PATH"
40-
git fetch origin 2>/dev/null || true
41-
git checkout --detach "origin/$MAIN_BRANCH" 2>/dev/null || true
42-
cd - > /dev/null
45+
# Kill existing tmux session if running
46+
if tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
47+
echo "Killing existing tmux session '$TMUX_SESSION'..."
48+
tmux kill-session -t "$TMUX_SESSION"
4349
fi
4450

45-
# If tmux session already exists, just attach
46-
if tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
47-
echo "Attaching to existing tmux session '$TMUX_SESSION'..."
48-
exec tmux attach-session -t "$TMUX_SESSION"
51+
# Remove existing worktree and create fresh
52+
if [ -d "$WORKTREE_PATH" ]; then
53+
echo "Removing existing worktree at $WORKTREE_PATH..."
54+
git worktree remove --force "$WORKTREE_PATH" 2>/dev/null || rm -rf "$WORKTREE_PATH"
4955
fi
56+
mkdir -p "$(dirname "$WORKTREE_PATH")"
57+
git fetch origin 2>/dev/null || true
58+
echo "Creating git worktree at $WORKTREE_PATH (branch: $MAIN_BRANCH)..."
59+
git worktree add "$WORKTREE_PATH" --detach "origin/$MAIN_BRANCH"
5060

5161
# Launch a new tmux session running this script inside the worktree
62+
# After the script exits, clean up the worktree
5263
echo "Starting tmux session '$TMUX_SESSION' in worktree $WORKTREE_PATH..."
5364
exec tmux new-session -s "$TMUX_SESSION" \
54-
"cd '$WORKTREE_PATH' && ETERNITY_LOOP_INSIDE=1 '$SCRIPT_DIR/eternity-loop.sh' $*; echo 'Eternity loop exited. Press enter to close.'; read"
65+
"cd '$WORKTREE_PATH' && ETERNITY_LOOP_INSIDE=1 ETERNITY_LOOP_WORKTREE='$WORKTREE_PATH' ETERNITY_LOOP_REPO_ROOT='$REPO_ROOT' LINEAR_API_KEY='${LINEAR_API_KEY:-}' '$SCRIPT_DIR/eternity-loop.sh' $*; echo 'Eternity loop exited. Press enter to close.'; read"
5566
fi
5667

5768
# --- From here on we're inside the tmux session, in the worktree ---
5869

59-
# Ensure Ctrl-C kills the entire process tree
60-
trap 'echo ""; echo "Interrupted. Cleaning up..."; kill 0; exit 130' INT TERM
70+
# Clean up the worktree on exit
71+
cleanup_worktree() {
72+
if [ -n "${ETERNITY_LOOP_WORKTREE:-}" ] && [ -n "${ETERNITY_LOOP_REPO_ROOT:-}" ]; then
73+
echo ""
74+
echo "Cleaning up worktree at $ETERNITY_LOOP_WORKTREE..."
75+
cd "$ETERNITY_LOOP_REPO_ROOT"
76+
git worktree remove --force "$ETERNITY_LOOP_WORKTREE" 2>/dev/null || rm -rf "$ETERNITY_LOOP_WORKTREE"
77+
echo "Worktree removed."
78+
fi
79+
}
80+
81+
# Ensure Ctrl-C kills the entire process tree and cleans up worktree
82+
trap 'echo ""; echo "Interrupted. Cleaning up..."; cleanup_worktree; kill 0; exit 130' INT TERM
83+
trap 'cleanup_worktree' EXIT
6184

6285
POLL_INTERVAL=60
6386
TOOL="claude"
@@ -72,19 +95,42 @@ log_err() {
7295
}
7396

7497
# Load .env if present (for LINEAR_API_KEY)
75-
ENV_FILE="$SCRIPT_DIR/.env"
76-
if [ -f "$ENV_FILE" ]; then
77-
set -a; source "$ENV_FILE"; set +a
98+
# Priority: .env.local > .env, checked in project root > scripts dir > original repo (when in worktree)
99+
for _env_candidate in \
100+
"$(pwd)/.env.local" \
101+
"$(pwd)/.env" \
102+
"$SCRIPT_DIR/.env.local" \
103+
"$SCRIPT_DIR/.env" \
104+
"${ETERNITY_LOOP_REPO_ROOT:-}/.env.local" \
105+
"${ETERNITY_LOOP_REPO_ROOT:-}/.env" \
106+
"${ETERNITY_LOOP_REPO_ROOT:-}/scripts/.env.local" \
107+
"${ETERNITY_LOOP_REPO_ROOT:-}/scripts/.env"; do
108+
[ -z "$_env_candidate" ] && continue
109+
if [ -f "$_env_candidate" ]; then
110+
set -a; source "$_env_candidate"; set +a
111+
break
112+
fi
113+
done
114+
115+
if [ -z "${LINEAR_API_KEY:-}" ]; then
116+
log "ERROR: LINEAR_API_KEY is not set. Set it in your shell environment or in a .env file (project root or scripts directory)."
117+
exit 1
78118
fi
79119

80120
linear_graphql() {
81121
local query="$1"
82122
local variables="${2:-"{}"}"
83123
[ -z "$variables" ] && variables="{}"
84-
curl -s -X POST https://api.linear.app/graphql \
124+
local payload
125+
payload="$(jq -n --arg q "$query" --argjson v "$variables" '{query: $q, variables: $v}')"
126+
log_err "[linear-api] Request: $(echo "$payload" | jq -c '.variables' 2>/dev/null || echo "$payload")"
127+
local response
128+
response=$(curl -s -X POST https://api.linear.app/graphql \
85129
-H "Content-Type: application/json" \
86130
-H "Authorization: $LINEAR_API_KEY" \
87-
-d "$(jq -n --arg q "$query" --argjson v "$variables" '{query: $q, variables: $v}')"
131+
-d "$payload")
132+
log_err "[linear-api] Response: $(echo "$response" | head -c 1000)"
133+
echo "$response"
88134
}
89135

90136
# Extract the first valid JSON object from mixed text (handles multiline, nested braces)

0 commit comments

Comments
 (0)