@kood/claude-code 0.7.10 → 0.7.12
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/dist/index.js +182 -90
- package/package.json +1 -1
- package/templates/.claude/agents/codex.md +40 -270
- package/templates/.claude/commands/cancel-ralph.md +18 -0
- package/templates/.claude/commands/git-all.md +12 -12
- package/templates/.claude/commands/git-merge.md +67 -0
- package/templates/.claude/commands/git-session.md +10 -10
- package/templates/.claude/commands/lint-fix.md +5 -5
- package/templates/.claude/commands/pre-deploy.md +7 -7
- package/templates/.claude/commands/ralph-loop.md +18 -0
- package/templates/.claude/commands/version-update.md +7 -7
- package/templates/.claude/hooks/ralph-stop-hook.sh +124 -0
- package/templates/.claude/hooks/session-env-setup.sh +33 -0
- package/templates/.claude/scripts/agent-teams/check-availability.sh +1 -1
- package/templates/.claude/scripts/agent-teams/setup-tmux.sh +1 -1
- package/templates/.claude/scripts/git/git-merge.sh +73 -0
- package/templates/.claude/scripts/setup-ralph-loop.sh +161 -0
- package/templates/.claude/skills/codex/SKILL.md +94 -469
- package/templates/.claude/skills/teams/SKILL.md +1 -1
- package/templates/.claude/skills/teams/references/fallback-strategy.md +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Ralph Loop Stop Hook
|
|
4
|
+
# Prevents session exit when a ralph-loop is active
|
|
5
|
+
# Feeds Claude's output back as input to continue the loop
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Read hook input from stdin (advanced stop hook API)
|
|
10
|
+
HOOK_INPUT=$(cat)
|
|
11
|
+
|
|
12
|
+
# Check if ralph-loop is active
|
|
13
|
+
RALPH_STATE_FILE=".claude/ralph-loop.local.md"
|
|
14
|
+
|
|
15
|
+
if [[ ! -f "$RALPH_STATE_FILE" ]]; then
|
|
16
|
+
# No active loop - allow exit
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Parse markdown frontmatter (YAML between ---) and extract values
|
|
21
|
+
FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE")
|
|
22
|
+
ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
|
|
23
|
+
MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//')
|
|
24
|
+
# Extract completion_promise and strip surrounding quotes if present
|
|
25
|
+
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
|
|
26
|
+
|
|
27
|
+
# Validate numeric fields before arithmetic operations
|
|
28
|
+
if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
|
|
29
|
+
echo "Warning: Ralph loop state file corrupted (iteration: '$ITERATION')" >&2
|
|
30
|
+
rm "$RALPH_STATE_FILE"
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then
|
|
35
|
+
echo "Warning: Ralph loop state file corrupted (max_iterations: '$MAX_ITERATIONS')" >&2
|
|
36
|
+
rm "$RALPH_STATE_FILE"
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Check if max iterations reached
|
|
41
|
+
if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then
|
|
42
|
+
echo "Ralph loop: Max iterations ($MAX_ITERATIONS) reached."
|
|
43
|
+
rm "$RALPH_STATE_FILE"
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Get transcript path from hook input
|
|
48
|
+
TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path')
|
|
49
|
+
|
|
50
|
+
if [[ ! -f "$TRANSCRIPT_PATH" ]]; then
|
|
51
|
+
echo "Warning: Ralph loop transcript not found" >&2
|
|
52
|
+
rm "$RALPH_STATE_FILE"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Read last assistant message from transcript (JSONL format)
|
|
57
|
+
if ! grep -q '"role":"assistant"' "$TRANSCRIPT_PATH"; then
|
|
58
|
+
echo "Warning: No assistant messages in transcript" >&2
|
|
59
|
+
rm "$RALPH_STATE_FILE"
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
LAST_LINE=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1)
|
|
64
|
+
if [[ -z "$LAST_LINE" ]]; then
|
|
65
|
+
rm "$RALPH_STATE_FILE"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
LAST_OUTPUT=$(echo "$LAST_LINE" | jq -r '
|
|
70
|
+
.message.content |
|
|
71
|
+
map(select(.type == "text")) |
|
|
72
|
+
map(.text) |
|
|
73
|
+
join("\n")
|
|
74
|
+
' 2>&1)
|
|
75
|
+
|
|
76
|
+
if [[ $? -ne 0 ]] || [[ -z "$LAST_OUTPUT" ]]; then
|
|
77
|
+
rm "$RALPH_STATE_FILE"
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Check for completion promise (only if set)
|
|
82
|
+
if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
|
|
83
|
+
PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g; s/\s+/ /g' 2>/dev/null || echo "")
|
|
84
|
+
|
|
85
|
+
if [[ -n "$PROMISE_TEXT" ]] && [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
|
|
86
|
+
echo "Ralph loop: Detected <promise>$COMPLETION_PROMISE</promise>"
|
|
87
|
+
rm "$RALPH_STATE_FILE"
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Not complete - continue loop with SAME PROMPT
|
|
93
|
+
NEXT_ITERATION=$((ITERATION + 1))
|
|
94
|
+
|
|
95
|
+
PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")
|
|
96
|
+
|
|
97
|
+
if [[ -z "$PROMPT_TEXT" ]]; then
|
|
98
|
+
echo "Warning: Ralph loop state file has no prompt" >&2
|
|
99
|
+
rm "$RALPH_STATE_FILE"
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# Update iteration (atomic write)
|
|
104
|
+
TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
|
|
105
|
+
sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
|
|
106
|
+
mv "$TEMP_FILE" "$RALPH_STATE_FILE"
|
|
107
|
+
|
|
108
|
+
# Build system message
|
|
109
|
+
if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
|
|
110
|
+
SYSTEM_MSG="Ralph iteration $NEXT_ITERATION | To stop: output <promise>$COMPLETION_PROMISE</promise> (ONLY when TRUE)"
|
|
111
|
+
else
|
|
112
|
+
SYSTEM_MSG="Ralph iteration $NEXT_ITERATION | No completion promise - loop runs until max iterations"
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
jq -n \
|
|
116
|
+
--arg prompt "$PROMPT_TEXT" \
|
|
117
|
+
--arg msg "$SYSTEM_MSG" \
|
|
118
|
+
'{
|
|
119
|
+
"decision": "block",
|
|
120
|
+
"reason": $prompt,
|
|
121
|
+
"systemMessage": $msg
|
|
122
|
+
}'
|
|
123
|
+
|
|
124
|
+
exit 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Session Environment Setup Hook
|
|
4
|
+
# Sets CLAUDE_SCRIPTS_ROOT based on where the command/skill is located
|
|
5
|
+
# - User scope (~/.claude/): CLAUDE_SCRIPTS_ROOT=~/.claude/scripts
|
|
6
|
+
# - Project scope (.claude/): CLAUDE_SCRIPTS_ROOT=.claude/scripts
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# Only run for SessionStart events
|
|
11
|
+
# The hook receives JSON input via stdin, but for SessionStart we just need to set env vars
|
|
12
|
+
|
|
13
|
+
# Determine the scripts root based on installation scope
|
|
14
|
+
# Check if user-level .claude exists and has scripts
|
|
15
|
+
USER_SCRIPTS_DIR="$HOME/.claude/scripts"
|
|
16
|
+
PROJECT_SCRIPTS_DIR=".claude/scripts"
|
|
17
|
+
|
|
18
|
+
# Priority: Project scope first (if exists), then User scope
|
|
19
|
+
if [[ -d "$PROJECT_SCRIPTS_DIR" ]]; then
|
|
20
|
+
SCRIPTS_ROOT="$PROJECT_SCRIPTS_DIR"
|
|
21
|
+
elif [[ -d "$USER_SCRIPTS_DIR" ]]; then
|
|
22
|
+
SCRIPTS_ROOT="$USER_SCRIPTS_DIR"
|
|
23
|
+
else
|
|
24
|
+
# Fallback to project scope (default)
|
|
25
|
+
SCRIPTS_ROOT="$PROJECT_SCRIPTS_DIR"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Write to CLAUDE_ENV_FILE (session-wide environment)
|
|
29
|
+
if [[ -n "${CLAUDE_ENV_FILE:-}" ]]; then
|
|
30
|
+
echo "CLAUDE_SCRIPTS_ROOT=$SCRIPTS_ROOT" >> "$CLAUDE_ENV_FILE"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
exit 0
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# git-merge.sh - 타겟 브랜치에 소스 브랜치를 merge 후 push
|
|
3
|
+
# Usage: ./git-merge.sh <target-branch> <source-branch>
|
|
4
|
+
# Example: ./git-merge.sh deploy/prod dev
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Git 저장소 확인
|
|
9
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
10
|
+
echo "Error: Not a git repository"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ $# -lt 2 ]; then
|
|
15
|
+
echo "Usage: $0 <target-branch> <source-branch>"
|
|
16
|
+
echo "Example: $0 deploy/prod dev"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
TARGET="$1"
|
|
21
|
+
SOURCE="$2"
|
|
22
|
+
ORIGINAL_BRANCH=$(git branch --show-current)
|
|
23
|
+
|
|
24
|
+
# Working directory clean 확인
|
|
25
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
26
|
+
echo "Error: Working directory is not clean. Commit or stash changes first."
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 소스 브랜치 존재 확인
|
|
31
|
+
if ! git rev-parse --verify "$SOURCE" >/dev/null 2>&1; then
|
|
32
|
+
echo "Error: Source branch '$SOURCE' does not exist"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# 타겟 브랜치 존재 확인
|
|
37
|
+
if ! git rev-parse --verify "$TARGET" >/dev/null 2>&1; then
|
|
38
|
+
echo "Error: Target branch '$TARGET' does not exist"
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo "=== Merging '$SOURCE' into '$TARGET' ==="
|
|
43
|
+
|
|
44
|
+
# remote 최신화
|
|
45
|
+
echo "--- Fetching origin ---"
|
|
46
|
+
git fetch origin
|
|
47
|
+
|
|
48
|
+
# 타겟 브랜치로 전환
|
|
49
|
+
echo "--- Checkout '$TARGET' ---"
|
|
50
|
+
git checkout "$TARGET"
|
|
51
|
+
|
|
52
|
+
# 타겟 브랜치 최신화
|
|
53
|
+
echo "--- Pulling latest '$TARGET' ---"
|
|
54
|
+
git pull origin "$TARGET" --ff-only || git pull origin "$TARGET"
|
|
55
|
+
|
|
56
|
+
# 머지 실행
|
|
57
|
+
echo "--- Merging '$SOURCE' into '$TARGET' ---"
|
|
58
|
+
if ! git merge "$SOURCE" --no-edit; then
|
|
59
|
+
echo "Error: Merge conflict detected. Resolve manually."
|
|
60
|
+
echo "You are now on '$TARGET' branch."
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 푸시
|
|
65
|
+
echo "--- Pushing '$TARGET' ---"
|
|
66
|
+
git push origin "$TARGET"
|
|
67
|
+
|
|
68
|
+
# 원래 브랜치로 복귀
|
|
69
|
+
echo "--- Returning to '$ORIGINAL_BRANCH' ---"
|
|
70
|
+
git checkout "$ORIGINAL_BRANCH"
|
|
71
|
+
|
|
72
|
+
echo ""
|
|
73
|
+
echo "Done: Merged '$SOURCE' into '$TARGET' and pushed."
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Ralph Loop Setup Script
|
|
4
|
+
# Creates state file + ensures Stop hook is registered in User settings
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# --- Hook auto-registration (User settings only, never Project) ---
|
|
9
|
+
ensure_stop_hook() {
|
|
10
|
+
local USER_SETTINGS="$HOME/.claude/settings.json"
|
|
11
|
+
local HOOK_CMD=".claude/hooks/ralph-stop-hook.sh"
|
|
12
|
+
|
|
13
|
+
# Create ~/.claude/ if needed
|
|
14
|
+
mkdir -p "$HOME/.claude"
|
|
15
|
+
|
|
16
|
+
# If no settings file, create with hook
|
|
17
|
+
if [[ ! -f "$USER_SETTINGS" ]]; then
|
|
18
|
+
cat > "$USER_SETTINGS" <<'HOOKEOF'
|
|
19
|
+
{
|
|
20
|
+
"hooks": {
|
|
21
|
+
"Stop": [
|
|
22
|
+
{
|
|
23
|
+
"hooks": [
|
|
24
|
+
{
|
|
25
|
+
"type": "command",
|
|
26
|
+
"command": ".claude/hooks/ralph-stop-hook.sh"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
HOOKEOF
|
|
34
|
+
echo "Created $USER_SETTINGS with Ralph stop hook"
|
|
35
|
+
return
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Check if ralph hook already registered (any path containing 'ralph')
|
|
39
|
+
if jq -e '.hooks.Stop // [] | .. | .command? // empty | test("ralph")' "$USER_SETTINGS" >/dev/null 2>&1; then
|
|
40
|
+
return # Already registered
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Add hook to existing settings (preserve all existing values)
|
|
44
|
+
local TEMP_FILE="${USER_SETTINGS}.tmp.$$"
|
|
45
|
+
|
|
46
|
+
# If Stop array exists, append to it; otherwise create it
|
|
47
|
+
if jq -e '.hooks.Stop' "$USER_SETTINGS" >/dev/null 2>&1; then
|
|
48
|
+
jq --arg cmd "$HOOK_CMD" '.hooks.Stop += [{"hooks": [{"type": "command", "command": $cmd}]}]' "$USER_SETTINGS" > "$TEMP_FILE"
|
|
49
|
+
elif jq -e '.hooks' "$USER_SETTINGS" >/dev/null 2>&1; then
|
|
50
|
+
jq --arg cmd "$HOOK_CMD" '.hooks.Stop = [{"hooks": [{"type": "command", "command": $cmd}]}]' "$USER_SETTINGS" > "$TEMP_FILE"
|
|
51
|
+
else
|
|
52
|
+
jq --arg cmd "$HOOK_CMD" '.hooks = {"Stop": [{"hooks": [{"type": "command", "command": $cmd}]}]}' "$USER_SETTINGS" > "$TEMP_FILE"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
mv "$TEMP_FILE" "$USER_SETTINGS"
|
|
56
|
+
echo "Registered Ralph stop hook in $USER_SETTINGS"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ensure_stop_hook
|
|
60
|
+
|
|
61
|
+
# --- Argument parsing ---
|
|
62
|
+
PROMPT_PARTS=()
|
|
63
|
+
MAX_ITERATIONS=0
|
|
64
|
+
COMPLETION_PROMISE="null"
|
|
65
|
+
|
|
66
|
+
while [[ $# -gt 0 ]]; do
|
|
67
|
+
case $1 in
|
|
68
|
+
-h|--help)
|
|
69
|
+
cat << 'HELP_EOF'
|
|
70
|
+
Ralph Loop - Interactive self-referential development loop
|
|
71
|
+
|
|
72
|
+
USAGE:
|
|
73
|
+
/ralph-loop [PROMPT...] [OPTIONS]
|
|
74
|
+
|
|
75
|
+
OPTIONS:
|
|
76
|
+
--max-iterations <n> Maximum iterations (default: unlimited)
|
|
77
|
+
--completion-promise '<text>' Promise phrase (USE QUOTES for multi-word)
|
|
78
|
+
-h, --help Show this help
|
|
79
|
+
|
|
80
|
+
EXAMPLES:
|
|
81
|
+
/ralph-loop Build a todo API --completion-promise 'DONE' --max-iterations 20
|
|
82
|
+
/ralph-loop --max-iterations 10 Fix the auth bug
|
|
83
|
+
/ralph-loop Refactor cache layer (runs forever)
|
|
84
|
+
|
|
85
|
+
STOPPING:
|
|
86
|
+
Only by reaching --max-iterations or detecting --completion-promise
|
|
87
|
+
|
|
88
|
+
MONITORING:
|
|
89
|
+
grep '^iteration:' .claude/ralph-loop.local.md
|
|
90
|
+
HELP_EOF
|
|
91
|
+
exit 0
|
|
92
|
+
;;
|
|
93
|
+
--max-iterations)
|
|
94
|
+
if [[ -z "${2:-}" ]] || ! [[ "$2" =~ ^[0-9]+$ ]]; then
|
|
95
|
+
echo "Error: --max-iterations requires a positive integer" >&2
|
|
96
|
+
exit 1
|
|
97
|
+
fi
|
|
98
|
+
MAX_ITERATIONS="$2"
|
|
99
|
+
shift 2
|
|
100
|
+
;;
|
|
101
|
+
--completion-promise)
|
|
102
|
+
if [[ -z "${2:-}" ]]; then
|
|
103
|
+
echo "Error: --completion-promise requires a text argument" >&2
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
COMPLETION_PROMISE="$2"
|
|
107
|
+
shift 2
|
|
108
|
+
;;
|
|
109
|
+
*)
|
|
110
|
+
PROMPT_PARTS+=("$1")
|
|
111
|
+
shift
|
|
112
|
+
;;
|
|
113
|
+
esac
|
|
114
|
+
done
|
|
115
|
+
|
|
116
|
+
PROMPT="${PROMPT_PARTS[*]}"
|
|
117
|
+
|
|
118
|
+
if [[ -z "$PROMPT" ]]; then
|
|
119
|
+
echo "Error: No prompt provided" >&2
|
|
120
|
+
echo "Usage: /ralph-loop <PROMPT> [--max-iterations N] [--completion-promise TEXT]" >&2
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# --- Create state file ---
|
|
125
|
+
mkdir -p .claude
|
|
126
|
+
|
|
127
|
+
if [[ -n "$COMPLETION_PROMISE" ]] && [[ "$COMPLETION_PROMISE" != "null" ]]; then
|
|
128
|
+
COMPLETION_PROMISE_YAML="\"$COMPLETION_PROMISE\""
|
|
129
|
+
else
|
|
130
|
+
COMPLETION_PROMISE_YAML="null"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
cat > .claude/ralph-loop.local.md <<EOF
|
|
134
|
+
---
|
|
135
|
+
active: true
|
|
136
|
+
iteration: 1
|
|
137
|
+
max_iterations: $MAX_ITERATIONS
|
|
138
|
+
completion_promise: $COMPLETION_PROMISE_YAML
|
|
139
|
+
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
$PROMPT
|
|
143
|
+
EOF
|
|
144
|
+
|
|
145
|
+
cat <<EOF
|
|
146
|
+
Ralph loop activated!
|
|
147
|
+
|
|
148
|
+
Iteration: 1
|
|
149
|
+
Max iterations: $(if [[ $MAX_ITERATIONS -gt 0 ]]; then echo $MAX_ITERATIONS; else echo "unlimited"; fi)
|
|
150
|
+
Completion promise: $(if [[ "$COMPLETION_PROMISE" != "null" ]]; then echo "$COMPLETION_PROMISE"; else echo "none (runs forever)"; fi)
|
|
151
|
+
|
|
152
|
+
The stop hook blocks exit and re-feeds the same prompt each iteration.
|
|
153
|
+
|
|
154
|
+
EOF
|
|
155
|
+
|
|
156
|
+
echo "$PROMPT"
|
|
157
|
+
|
|
158
|
+
if [[ "$COMPLETION_PROMISE" != "null" ]]; then
|
|
159
|
+
echo ""
|
|
160
|
+
echo "To complete: output <promise>$COMPLETION_PROMISE</promise> (ONLY when TRUE)"
|
|
161
|
+
fi
|