sx-loops: each language runs in its own git worktree
Previous version ran all 7 claude sessions in the main working tree on branch 'architecture'. That would race on git operations and cross- contaminate commits between languages even though their file scopes don't overlap. Now each session runs in /root/rose-ash-loops/<lang> on branch loops/<lang>, created from the current architecture HEAD. sx-loops-down.sh gains --clean to remove the worktrees; loops/<lang> branches stay unless explicitly deleted. Also: second Enter keystroke after the /loop command, since Claude's input box sometimes interprets the first newline as a soft break.
This commit is contained in:
@@ -1,23 +1,43 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Stop the sx-loops tmux session. Prompts the claude sessions to exit cleanly
|
# Stop the sx-loops tmux session. Optionally (--clean) also remove the
|
||||||
# first (/exit in each window), then kills the tmux session after a grace
|
# per-language git worktrees. Loops stop immediately; in-progress iterations
|
||||||
# period. Loops stop immediately; in-progress iterations commit what they have.
|
# commit whatever they have; nothing is pushed that wasn't already.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SESSION="sx-loops"
|
SESSION="sx-loops"
|
||||||
|
WORKTREE_BASE="/root/rose-ash-loops"
|
||||||
|
CLEAN=0
|
||||||
|
|
||||||
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
for arg in "$@"; do
|
||||||
echo "No sx-loops session running."
|
case "$arg" in
|
||||||
exit 0
|
--clean) CLEAN=1 ;;
|
||||||
fi
|
*) echo "Unknown arg: $arg"; exit 2 ;;
|
||||||
|
esac
|
||||||
WINDOWS=$(tmux list-windows -t "$SESSION" -F '#W')
|
|
||||||
for w in $WINDOWS; do
|
|
||||||
tmux send-keys -t "$SESSION:$w" "/exit" C-m 2>/dev/null || true
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Sent /exit to all windows. Waiting 5s for clean shutdown..."
|
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
||||||
sleep 5
|
WINDOWS=$(tmux list-windows -t "$SESSION" -F '#W')
|
||||||
|
for w in $WINDOWS; do
|
||||||
|
tmux send-keys -t "$SESSION:$w" "/exit" C-m 2>/dev/null || true
|
||||||
|
done
|
||||||
|
echo "Sent /exit to all windows. Waiting 5s for clean shutdown..."
|
||||||
|
sleep 5
|
||||||
|
tmux kill-session -t "$SESSION"
|
||||||
|
echo "Killed tmux session '$SESSION'."
|
||||||
|
else
|
||||||
|
echo "No sx-loops tmux session running."
|
||||||
|
fi
|
||||||
|
|
||||||
tmux kill-session -t "$SESSION"
|
if [ "$CLEAN" = "1" ]; then
|
||||||
echo "Killed sx-loops session."
|
cd "$(dirname "$0")/.."
|
||||||
|
for lang in lua prolog forth erlang haskell js hs; do
|
||||||
|
wt="$WORKTREE_BASE/$lang"
|
||||||
|
if [ -d "$wt" ]; then
|
||||||
|
git worktree remove --force "$wt" 2>/dev/null || rm -rf "$wt"
|
||||||
|
echo "Removed worktree: $wt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
git worktree prune
|
||||||
|
echo "Worktree branches (loops/<lang>) are preserved. Delete manually if desired:"
|
||||||
|
echo " git branch -D loops/lua loops/prolog loops/forth loops/erlang loops/haskell loops/js loops/hs"
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,25 +1,32 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Spawn 7 claude sessions in tmux, one per language loop.
|
# Spawn 7 claude sessions in tmux, one per language loop.
|
||||||
# Each runs /loop against its briefing, doing ONE iteration per fire.
|
# Each runs in its own git worktree rooted at /root/rose-ash-loops/<lang>,
|
||||||
|
# on branch loops/<lang>. No two loops share a working tree, so there's
|
||||||
|
# zero risk of file collisions between languages.
|
||||||
#
|
#
|
||||||
# Usage: ./scripts/sx-loops-up.sh [interval]
|
# Usage: ./scripts/sx-loops-up.sh [interval]
|
||||||
# interval defaults to self-paced (omit to let model decide)
|
# interval defaults to self-paced (omit to let model decide)
|
||||||
#
|
#
|
||||||
# After the script prints done:
|
# After the script prints done:
|
||||||
# tmux a -t sx-loops
|
# tmux a -t sx-loops
|
||||||
# Ctrl-B + <window-number> to switch
|
# Ctrl-B + <window-number> to switch (0=lua ... 6=hs)
|
||||||
# Ctrl-B + d to detach (loops keep running)
|
# Ctrl-B + d to detach (loops keep running, SSH-safe)
|
||||||
|
#
|
||||||
|
# Stop: ./scripts/sx-loops-down.sh
|
||||||
|
# Wipe worktrees too: ./scripts/sx-loops-down.sh --clean
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
SESSION="sx-loops"
|
SESSION="sx-loops"
|
||||||
|
WORKTREE_BASE="/root/rose-ash-loops"
|
||||||
INTERVAL="${1:-}" # e.g. "15m", or empty for self-paced
|
INTERVAL="${1:-}" # e.g. "15m", or empty for self-paced
|
||||||
BOOT_WAIT=20 # seconds to wait for each claude to boot before sending /loop
|
BOOT_WAIT=20
|
||||||
|
|
||||||
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
||||||
echo "Session '$SESSION' already exists."
|
echo "Session '$SESSION' already exists."
|
||||||
echo " Attach: tmux a -t $SESSION"
|
echo " Attach: tmux a -t $SESSION"
|
||||||
echo " Kill: tmux kill-session -t $SESSION"
|
echo " Kill: ./scripts/sx-loops-down.sh"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -34,39 +41,62 @@ declare -A BRIEFING=(
|
|||||||
)
|
)
|
||||||
ORDER=(lua prolog forth erlang haskell js hs)
|
ORDER=(lua prolog forth erlang haskell js hs)
|
||||||
|
|
||||||
# Create tmux session with 7 windows, one per language
|
mkdir -p "$WORKTREE_BASE"
|
||||||
tmux new-session -d -s "$SESSION" -n "${ORDER[0]}" -c "$PWD"
|
|
||||||
for lang in "${ORDER[@]:1}"; do
|
echo "Preparing per-language worktrees under $WORKTREE_BASE ..."
|
||||||
tmux new-window -t "$SESSION" -n "$lang" -c "$PWD"
|
for lang in "${ORDER[@]}"; do
|
||||||
|
wt="$WORKTREE_BASE/$lang"
|
||||||
|
branch="loops/$lang"
|
||||||
|
if [ -d "$wt/.git" ] || [ -f "$wt/.git" ]; then
|
||||||
|
echo " $lang: worktree exists at $wt"
|
||||||
|
else
|
||||||
|
# Create or reset the branch at current architecture HEAD, then add worktree
|
||||||
|
if git show-ref --verify --quiet "refs/heads/$branch"; then
|
||||||
|
git worktree add "$wt" "$branch" >/dev/null
|
||||||
|
else
|
||||||
|
git worktree add -b "$branch" "$wt" architecture >/dev/null
|
||||||
|
fi
|
||||||
|
echo " $lang: worktree created at $wt on $branch"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create tmux session with 7 windows, each cwd in its worktree
|
||||||
|
tmux new-session -d -s "$SESSION" -n "${ORDER[0]}" -c "$WORKTREE_BASE/${ORDER[0]}"
|
||||||
|
for lang in "${ORDER[@]:1}"; do
|
||||||
|
tmux new-window -t "$SESSION" -n "$lang" -c "$WORKTREE_BASE/$lang"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Start claude in each window
|
|
||||||
echo "Starting 7 claude sessions..."
|
echo "Starting 7 claude sessions..."
|
||||||
for lang in "${ORDER[@]}"; do
|
for lang in "${ORDER[@]}"; do
|
||||||
tmux send-keys -t "$SESSION:$lang" "claude" C-m
|
tmux send-keys -t "$SESSION:$lang" "claude" C-m
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Waiting ${BOOT_WAIT}s for claude to boot in each window..."
|
echo "Waiting ${BOOT_WAIT}s for claude to boot..."
|
||||||
sleep "$BOOT_WAIT"
|
sleep "$BOOT_WAIT"
|
||||||
|
|
||||||
# Send the /loop command in each window
|
|
||||||
for lang in "${ORDER[@]}"; do
|
for lang in "${ORDER[@]}"; do
|
||||||
briefing="${BRIEFING[$lang]}"
|
briefing="${BRIEFING[$lang]}"
|
||||||
if [ -n "$INTERVAL" ]; then
|
if [ -n "$INTERVAL" ]; then
|
||||||
cmd="/loop $INTERVAL Read plans/agent-briefings/$briefing and do ONE iteration per fire: pick the first unchecked [ ] from the plan, implement, test, commit with a short factual message, tick the box, append one dated line to the Progress log (newest first), then stop. Push to origin/loops/$lang (branch per language; never main). Stay strictly in the briefing's scope. Never push to main."
|
preamble="/loop $INTERVAL "
|
||||||
else
|
else
|
||||||
cmd="/loop Read plans/agent-briefings/$briefing and do ONE iteration per fire: pick the first unchecked [ ] from the plan, implement, test, commit with a short factual message, tick the box, append one dated line to the Progress log (newest first), then stop. Push to origin/loops/$lang (branch per language; never main). Stay strictly in the briefing's scope. Never push to main."
|
preamble="/loop "
|
||||||
fi
|
fi
|
||||||
tmux send-keys -t "$SESSION:$lang" "$cmd" C-m
|
cmd="${preamble}Read plans/agent-briefings/$briefing and do ONE iteration per fire: pick the first unchecked [ ] from the plan, implement, test, commit with a short factual message, tick the box, append one dated line to the Progress log (newest first), then stop. You are on branch loops/$lang in worktree /root/rose-ash-loops/$lang; push commits to origin/loops/$lang (never main). Stay strictly in the briefing's scope."
|
||||||
|
tmux send-keys -t "$SESSION:$lang" "$cmd"
|
||||||
|
# tiny pause, then a second Enter — Claude's input box sometimes eats the first newline as a soft break
|
||||||
|
sleep 0.5
|
||||||
|
tmux send-keys -t "$SESSION:$lang" Enter
|
||||||
done
|
done
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Done. 7 loops started in tmux session '$SESSION'."
|
echo "Done. 7 loops started in tmux session '$SESSION', each in its own worktree."
|
||||||
echo ""
|
echo ""
|
||||||
echo " Attach: tmux a -t $SESSION"
|
echo " Attach: tmux a -t $SESSION"
|
||||||
echo " Switch: Ctrl-B then window number 0-6 (lua=0, prolog=1, forth=2, erlang=3, haskell=4, js=5, hs=6)"
|
echo " Switch: Ctrl-B <0..6> (0=lua 1=prolog 2=forth 3=erlang 4=haskell 5=js 6=hs)"
|
||||||
echo " Or index: Ctrl-B w (lists windows)"
|
echo " List: Ctrl-B w"
|
||||||
echo " Detach: Ctrl-B d"
|
echo " Detach: Ctrl-B d"
|
||||||
echo " Kill all: tmux kill-session -t $SESSION"
|
echo " Stop: ./scripts/sx-loops-down.sh"
|
||||||
|
echo " Capture: tmux capture-pane -t $SESSION:<lang> -p | tail -40"
|
||||||
echo ""
|
echo ""
|
||||||
echo "If a window did NOT enter /loop mode (boot was slow), attach and paste the /loop command manually."
|
echo "Worktrees:"
|
||||||
|
git worktree list | grep -E "rose-ash-loops/" || true
|
||||||
|
|||||||
Reference in New Issue
Block a user