agentic-loop 3.26.0 → 3.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/tab-rename.md +33 -10
- package/.claude/skills/prd/SKILL.md +25 -20
- package/bin/ralph.sh +6 -0
- package/package.json +1 -1
- package/ralph/loop.sh +8 -6
- package/ralph/setup.sh +46 -1
- package/ralph/utils.sh +22 -3
|
@@ -6,8 +6,6 @@ description: Rename the current terminal tab so you can tell your Claude Code ta
|
|
|
6
6
|
|
|
7
7
|
The user wants to rename the current terminal tab. This is useful when you have multiple Claude Code sessions open and every tab just shows "...skip-permissions".
|
|
8
8
|
|
|
9
|
-
> **Note:** This uses AppleScript and only works in macOS Terminal.app and iTerm2.
|
|
10
|
-
|
|
11
9
|
## Step 1: Determine the Tab Name
|
|
12
10
|
|
|
13
11
|
Check if the user provided an argument: `$ARGUMENTS`
|
|
@@ -27,20 +25,47 @@ If the user selects "Other", use their custom text as the tab name.
|
|
|
27
25
|
|
|
28
26
|
## Step 2: Set the Tab Title
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
Use `$TERM_PROGRAM` (via Bash: `echo $TERM_PROGRAM`) to detect the terminal, then apply the right method. **Important:** Escape any double quotes in the tab name before embedding in AppleScript strings.
|
|
29
|
+
|
|
30
|
+
### iTerm2 (`TERM_PROGRAM=iTerm.app`)
|
|
31
|
+
|
|
32
|
+
Use iTerm2's proprietary escape sequence — this is the most reliable method:
|
|
31
33
|
|
|
32
34
|
```bash
|
|
33
|
-
|
|
34
|
-
osascript -e 'tell application "Terminal" to set custom title of selected tab of front window to "TAB_NAME"' 2>/dev/null
|
|
35
|
+
printf '\033]1337;SetUserVar=tab_title=%s\007' "$(echo -n 'TAB_NAME' | base64)"
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
Then also set the session name via osascript as a fallback:
|
|
38
39
|
|
|
39
40
|
```bash
|
|
40
41
|
osascript -e 'tell application "iTerm2" to tell current session of current window to set name to "TAB_NAME"' 2>/dev/null
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
### Terminal.app (`TERM_PROGRAM=Apple_Terminal`)
|
|
45
|
+
|
|
46
|
+
Terminal.app requires **two steps** — set the custom title AND enable the custom title display (otherwise the shell's auto-title overrides it):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
osascript -e '
|
|
50
|
+
tell application "Terminal"
|
|
51
|
+
set t to selected tab of front window
|
|
52
|
+
set custom title of t to "TAB_NAME"
|
|
53
|
+
end tell' 2>/dev/null
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then use the ANSI escape to set the window/tab title (this is what actually sticks):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
printf '\033]0;TAB_NAME\007'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Other terminals / fallback
|
|
63
|
+
|
|
64
|
+
Use the standard ANSI escape sequence:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
printf '\033]0;TAB_NAME\007'
|
|
68
|
+
```
|
|
44
69
|
|
|
45
70
|
## Step 3: Confirm
|
|
46
71
|
|
|
@@ -48,6 +73,4 @@ If the rename succeeded, say:
|
|
|
48
73
|
|
|
49
74
|
"Tab renamed to **{tab_name}**."
|
|
50
75
|
|
|
51
|
-
If
|
|
52
|
-
|
|
53
|
-
"Tab renaming requires macOS Terminal.app or iTerm2. On other terminals, you can set the tab title manually with: `printf '\033]0;my-title\007'`"
|
|
76
|
+
> **Tip:** If your shell resets the title on each prompt (common with oh-my-zsh), add `export DISABLE_AUTO_TITLE="true"` to your `~/.zshrc`, then restart your shell.
|
|
@@ -24,9 +24,11 @@ $ARGUMENTS
|
|
|
24
24
|
ls docs/ideas/*.md 2>/dev/null || echo "No idea files found"
|
|
25
25
|
ls docs/plans/*.md 2>/dev/null || echo "No plan files found"
|
|
26
26
|
```
|
|
27
|
-
2.
|
|
28
|
-
-
|
|
29
|
-
-
|
|
27
|
+
2. If source files exist, use AskUserQuestion to let the user pick:
|
|
28
|
+
- **Question:** "What should I build the PRD from?"
|
|
29
|
+
- **Header:** "PRD source"
|
|
30
|
+
- **Options:** List discovered idea/plan files (up to 3-4 most relevant), plus a "Describe a feature" option that says "Type a description directly (e.g., 'Add user logout button')"
|
|
31
|
+
- If no source files found, skip AskUserQuestion and just say: "Describe the feature you'd like to build (e.g., `/prd 'Add user logout button'`)"
|
|
30
32
|
|
|
31
33
|
**If `$ARGUMENTS` looks like a plan file** (`plans/` prefix, `docs/plans/` path, or full path to a plan file):
|
|
32
34
|
- If it's a full path, use it directly
|
|
@@ -122,9 +124,13 @@ Say: "Before I generate stories, I want to make sure we've covered the key areas
|
|
|
122
124
|
- Phases: What's the logical order?
|
|
123
125
|
- Verification: What commands prove each phase worked?
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
After presenting the hardening questions, use AskUserQuestion:
|
|
128
|
+
- **Question:** "Answer the questions above, or proceed with sensible defaults?"
|
|
129
|
+
- **Header:** "Hardening"
|
|
130
|
+
- **Options:**
|
|
131
|
+
- **"Go with defaults"** — "Proceed with sensible defaults for unanswered questions"
|
|
132
|
+
- **"Let me answer"** — "I'll respond to the questions above"
|
|
133
|
+
- If the user selects "Let me answer" or "Other", **STOP and wait for their response** before continuing.
|
|
128
134
|
|
|
129
135
|
### Step 3: Check for Existing PRD
|
|
130
136
|
|
|
@@ -133,16 +139,11 @@ cat .ralph/prd.json 2>/dev/null
|
|
|
133
139
|
```
|
|
134
140
|
|
|
135
141
|
If it exists, read it and say:
|
|
136
|
-
"`.ralph/prd.json` exists with {N} stories ({M} completed, {P} pending).
|
|
137
|
-
|
|
138
|
-
Options:
|
|
139
|
-
- **'append'** - Add new stories to the existing PRD (recommended)
|
|
140
|
-
- **'overwrite'** - Replace it entirely
|
|
141
|
-
- **'cancel'** - Stop here"
|
|
142
|
+
"`.ralph/prd.json` exists with {N} stories ({M} completed, {P} pending). I'll append new stories to it."
|
|
142
143
|
|
|
143
|
-
**
|
|
144
|
+
**Default behavior is append** — just proceed. Do NOT ask for confirmation unless the user explicitly says "overwrite" or "replace".
|
|
144
145
|
|
|
145
|
-
|
|
146
|
+
When appending:
|
|
146
147
|
- Find highest existing story number (ignore prefix - could be US-005 or TASK-005)
|
|
147
148
|
- **Always use TASK- prefix** for new stories (e.g., if highest is US-005 or TASK-005, new stories start at TASK-006)
|
|
148
149
|
- New stories will be added after existing ones
|
|
@@ -332,14 +333,18 @@ Open the PRD for review:
|
|
|
332
333
|
open -a TextEdit .ralph/prd.json
|
|
333
334
|
```
|
|
334
335
|
|
|
335
|
-
Say: "I've {created|updated} the PRD with {N} stories and opened it in TextEdit.
|
|
336
|
+
Say: "I've {created|updated} the PRD with {N} stories and opened it in TextEdit."
|
|
336
337
|
|
|
337
|
-
|
|
338
|
-
- **
|
|
339
|
-
- **
|
|
340
|
-
-
|
|
338
|
+
Then use AskUserQuestion with **multiSelect: true**:
|
|
339
|
+
- **Question:** "How does the PRD look?"
|
|
340
|
+
- **Header:** "PRD review"
|
|
341
|
+
- **multiSelect:** true
|
|
342
|
+
- **Options:**
|
|
343
|
+
- **"Approved"** — "PRD is good — ready to run with Ralph"
|
|
344
|
+
- **"Edit"** — "I'll tell you what to change"
|
|
345
|
+
- **"I edited the JSON"** — "I made changes directly in the file, re-validate it"
|
|
341
346
|
|
|
342
|
-
**STOP and wait for
|
|
347
|
+
If the user selects "Edit" (with or without other selections), **STOP and wait for their changes**. If "I edited the JSON" is selected, re-read and re-validate the PRD. If only "Approved" is selected, proceed to Step 9.
|
|
343
348
|
|
|
344
349
|
### Step 9: Final Instructions
|
|
345
350
|
|
package/bin/ralph.sh
CHANGED
|
@@ -152,6 +152,12 @@ main() {
|
|
|
152
152
|
echo ""
|
|
153
153
|
exit 1
|
|
154
154
|
fi
|
|
155
|
+
# Auto-refresh skills/hooks/signs when the package version changes
|
|
156
|
+
local version_file="$RALPH_DIR/.last_version"
|
|
157
|
+
if [[ ! -f "$version_file" ]] || [[ "$(cat "$version_file" 2>/dev/null)" != "$RALPH_VERSION" ]]; then
|
|
158
|
+
setup_refresh
|
|
159
|
+
echo "$RALPH_VERSION" > "$version_file"
|
|
160
|
+
fi
|
|
155
161
|
# Clear any previous stop signal
|
|
156
162
|
rm -f "$RALPH_DIR/.stop"
|
|
157
163
|
run_loop "$@"
|
package/package.json
CHANGED
package/ralph/loop.sh
CHANGED
|
@@ -92,7 +92,8 @@ check_for_updates() {
|
|
|
92
92
|
if $update_cmd 2>&1 | tail -3; then
|
|
93
93
|
print_success " Updated to v$latest — restarting..."
|
|
94
94
|
echo ""
|
|
95
|
-
# Re-exec ralph.sh
|
|
95
|
+
# Re-exec ralph.sh — version-change detection in the run path
|
|
96
|
+
# will auto-refresh skills, hooks, and signs from the new package
|
|
96
97
|
local ralph_bin
|
|
97
98
|
ralph_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")/../bin" && pwd)/ralph.sh"
|
|
98
99
|
exec "$ralph_bin" run "$@"
|
|
@@ -681,8 +682,8 @@ _docker_safety_warning() {
|
|
|
681
682
|
echo " ║ and executes migrations without asking. ║"
|
|
682
683
|
echo " ║ ║"
|
|
683
684
|
echo " ║ Without Docker, Ralph operates directly on your local ║"
|
|
684
|
-
echo " ║ machine. A
|
|
685
|
-
echo " ║
|
|
685
|
+
echo " ║ machine. A runaway command can affect services outside ║"
|
|
686
|
+
echo " ║ this repo. ║"
|
|
686
687
|
echo " ║ ║"
|
|
687
688
|
echo " ║ With Docker, your project's services are isolated: ║"
|
|
688
689
|
echo " ║ - Databases and caches run in containers, not locally ║"
|
|
@@ -722,8 +723,9 @@ _docker_safety_warning() {
|
|
|
722
723
|
}
|
|
723
724
|
|
|
724
725
|
run_loop() {
|
|
725
|
-
# Save original args for update restart
|
|
726
|
-
local _original_args=(
|
|
726
|
+
# Save original args for update restart (explicit empty init for Bash 3.2 compatibility)
|
|
727
|
+
local _original_args=()
|
|
728
|
+
[[ $# -gt 0 ]] && _original_args=("$@")
|
|
727
729
|
|
|
728
730
|
# PID of the currently running Claude pipeline (used by trap to kill it)
|
|
729
731
|
_CLAUDE_PIPELINE_PID=""
|
|
@@ -794,7 +796,7 @@ run_loop() {
|
|
|
794
796
|
check_dependencies
|
|
795
797
|
|
|
796
798
|
# Check for newer version on npm (once per day, non-blocking if offline)
|
|
797
|
-
check_for_updates "${_original_args[@]}"
|
|
799
|
+
check_for_updates "${_original_args[@]+"${_original_args[@]}"}"
|
|
798
800
|
|
|
799
801
|
# Warn if no Docker compose file (safety net for autonomous execution)
|
|
800
802
|
_docker_safety_warning
|
package/ralph/setup.sh
CHANGED
|
@@ -118,6 +118,50 @@ _migrate_credentials_to_env() {
|
|
|
118
118
|
echo ""
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
# Lightweight refresh after auto-update — syncs files from the new package version
|
|
122
|
+
# without re-running interactive or first-time-only setup steps.
|
|
123
|
+
setup_refresh() {
|
|
124
|
+
local pkg_root
|
|
125
|
+
pkg_root="$(cd "$RALPH_LIB/.." && pwd)"
|
|
126
|
+
|
|
127
|
+
echo " Syncing updated files..."
|
|
128
|
+
|
|
129
|
+
# Re-copy slash commands/skills
|
|
130
|
+
setup_slash_commands "$pkg_root"
|
|
131
|
+
|
|
132
|
+
# Re-copy hooks (scripts + settings.json wiring)
|
|
133
|
+
setup_claude_hooks "$pkg_root"
|
|
134
|
+
|
|
135
|
+
# Merge any new default signs
|
|
136
|
+
if [[ -f ".ralph/signs.json" ]] && [[ -f "$pkg_root/templates/signs.json" ]] && command -v jq &>/dev/null; then
|
|
137
|
+
local existing_ids new_signs_added=0
|
|
138
|
+
existing_ids=$(jq -r '.signs[].id // empty' ".ralph/signs.json" 2>/dev/null | tr '\n' '|')
|
|
139
|
+
|
|
140
|
+
while IFS= read -r sign; do
|
|
141
|
+
local sign_id
|
|
142
|
+
sign_id=$(echo "$sign" | jq -r '.id // empty')
|
|
143
|
+
if [[ -n "$sign_id" && ! "$existing_ids" =~ "$sign_id" ]]; then
|
|
144
|
+
local tmp_file
|
|
145
|
+
tmp_file=$(mktemp)
|
|
146
|
+
jq --argjson new_sign "$sign" '.signs += [$new_sign]' ".ralph/signs.json" > "$tmp_file"
|
|
147
|
+
mv "$tmp_file" ".ralph/signs.json"
|
|
148
|
+
((new_signs_added++)) || true
|
|
149
|
+
fi
|
|
150
|
+
done < <(jq -c '.signs[]' "$pkg_root/templates/signs.json" 2>/dev/null)
|
|
151
|
+
|
|
152
|
+
if [[ $new_signs_added -gt 0 ]]; then
|
|
153
|
+
echo " Merged $new_signs_added new sign(s)"
|
|
154
|
+
fi
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# Append any new PROMPT.md sections
|
|
158
|
+
if [[ -f "PROMPT.md" ]] && [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
|
|
159
|
+
append_prompt_sections "$pkg_root/templates/PROMPT.md" "PROMPT.md"
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
print_success " Setup refreshed"
|
|
163
|
+
}
|
|
164
|
+
|
|
121
165
|
ralph_setup() {
|
|
122
166
|
echo ""
|
|
123
167
|
echo " _ _ _ _ "
|
|
@@ -252,7 +296,7 @@ setup_ralph_dir() {
|
|
|
252
296
|
tmp_file=$(mktemp)
|
|
253
297
|
jq --argjson new_sign "$sign" '.signs += [$new_sign]' ".ralph/signs.json" > "$tmp_file"
|
|
254
298
|
mv "$tmp_file" ".ralph/signs.json"
|
|
255
|
-
((new_signs_added++))
|
|
299
|
+
((new_signs_added++)) || true
|
|
256
300
|
fi
|
|
257
301
|
done < <(jq -c '.signs[]' "$pkg_root/templates/signs.json" 2>/dev/null)
|
|
258
302
|
|
|
@@ -312,6 +356,7 @@ setup_gitignore() {
|
|
|
312
356
|
".ralph/tool-log.txt"
|
|
313
357
|
".ralph/suggested-signs.txt"
|
|
314
358
|
".ralph/.preflight_cache"
|
|
359
|
+
".ralph/.last_version"
|
|
315
360
|
".ralph/.lock"
|
|
316
361
|
".backups/"
|
|
317
362
|
".claude/settings.json"
|
package/ralph/utils.sh
CHANGED
|
@@ -58,6 +58,7 @@ NC='\033[0m' # No Color
|
|
|
58
58
|
# window running the script. In practice this works because the user just
|
|
59
59
|
# ran the command, but rapid window switching can cause a mismatch.
|
|
60
60
|
_ORIGINAL_TERMINAL_BG=""
|
|
61
|
+
_ORIGINAL_TERMINAL_FG=""
|
|
61
62
|
|
|
62
63
|
set_terminal_bg() {
|
|
63
64
|
local hex="${1:-#1a2e2e}" # Default: subtle dark teal
|
|
@@ -65,8 +66,9 @@ set_terminal_bg() {
|
|
|
65
66
|
# Only works in Terminal.app
|
|
66
67
|
[[ "$TERM_PROGRAM" != "Apple_Terminal" ]] && return 0
|
|
67
68
|
|
|
68
|
-
# Save current background
|
|
69
|
+
# Save current background and text colors for restore
|
|
69
70
|
_ORIGINAL_TERMINAL_BG=$(osascript -e 'tell application "Terminal" to get background color of front window' 2>/dev/null) || return 0
|
|
71
|
+
_ORIGINAL_TERMINAL_FG=$(osascript -e 'tell application "Terminal" to get normal text color of front window' 2>/dev/null) || true
|
|
70
72
|
|
|
71
73
|
# Parse hex to 16-bit RGB values (Terminal.app uses 0-65535 range)
|
|
72
74
|
local r=$((16#${hex:1:2} * 257))
|
|
@@ -74,6 +76,18 @@ set_terminal_bg() {
|
|
|
74
76
|
local b=$((16#${hex:5:2} * 257))
|
|
75
77
|
|
|
76
78
|
osascript -e "tell application \"Terminal\" to set background color of front window to {$r, $g, $b}" 2>/dev/null || true
|
|
79
|
+
|
|
80
|
+
# If the background is dark, ensure text is light enough to read
|
|
81
|
+
# Calculate perceived brightness: (R*299 + G*587 + B*114) / 1000 (8-bit scale)
|
|
82
|
+
local r8=$((16#${hex:1:2}))
|
|
83
|
+
local g8=$((16#${hex:3:2}))
|
|
84
|
+
local b8=$((16#${hex:5:2}))
|
|
85
|
+
local brightness=$(( (r8 * 299 + g8 * 587 + b8 * 114) / 1000 ))
|
|
86
|
+
|
|
87
|
+
if [[ $brightness -lt 128 ]]; then
|
|
88
|
+
# Dark background — set light text (soft white: #e0e0e0)
|
|
89
|
+
osascript -e 'tell application "Terminal" to set normal text color of front window to {57568, 57568, 57568}' 2>/dev/null || true
|
|
90
|
+
fi
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
restore_terminal_bg() {
|
|
@@ -82,17 +96,22 @@ restore_terminal_bg() {
|
|
|
82
96
|
|
|
83
97
|
osascript -e "tell application \"Terminal\" to set background color of front window to {$_ORIGINAL_TERMINAL_BG}" 2>/dev/null || true
|
|
84
98
|
_ORIGINAL_TERMINAL_BG=""
|
|
99
|
+
|
|
100
|
+
if [[ -n "$_ORIGINAL_TERMINAL_FG" ]]; then
|
|
101
|
+
osascript -e "tell application \"Terminal\" to set normal text color of front window to {$_ORIGINAL_TERMINAL_FG}" 2>/dev/null || true
|
|
102
|
+
_ORIGINAL_TERMINAL_FG=""
|
|
103
|
+
fi
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
# Set terminal tab title (works in Terminal.app, iTerm2, and most xterm-compatible terminals)
|
|
88
107
|
set_tab_title() {
|
|
89
108
|
local title="$1"
|
|
90
|
-
printf '\033]0;%s\007' "$title"
|
|
109
|
+
printf '\033]0;%s\007' "$title" >&2
|
|
91
110
|
}
|
|
92
111
|
|
|
93
112
|
# Restore tab title to default (empty = terminal decides)
|
|
94
113
|
restore_tab_title() {
|
|
95
|
-
printf '\033]0;\007'
|
|
114
|
+
printf '\033]0;\007' >&2
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
# Get existing frontend directories in this project
|