1616set -euo pipefail
1717
1818SCRIPT_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} "
2021WORKTREE_NAME=" eternity-loop"
2122
2223# --- Worktree + tmux bootstrap ---
2324# If not already inside the eternity-loop tmux session, set up worktree and launch
2425if [ -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"
5566fi
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
6285POLL_INTERVAL=60
6386TOOL=" 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
78118fi
79119
80120linear_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