┌─┐┌┐┌┬ ┬┌┬┐┬ ┬┬┌┐┌┌─┐ ┌─┐┬ ┬
├─┤│││└┬┘ │ ├─┤│││││ ┬ └─┐├─┤
┴ ┴┘└┘ ┴  ┴ ┴ ┴┴┘└┘└─┘o└─┘┴ ┴
v0.1.0-alphaAutopoietic Execution Loop

the bash script is the context

github.com/xertrov/anything.sh
Here Be DragonsThis script executes LLM-generated code without confirmation. It will cheerfully delete your files, email your boss, or reorganise your music collection by astrological sign. The slime mold does not ask permission. Use in a VM or container.

NAME

anything.sh — the slime mold of bash.

SYNOPSIS

./anything.sh ["make todd proud and release skyrim for bash, immersive TUI, atomospheric, action RPG"]

ENVIRONMENT

ANYTHING_EXTRA — Optional. Inject custom context into the LLM prompt.

Use to advertise platform-specific tools (e.g. say on macOS), inform about installed utilities, or add task-specific instructions.

# Example: macOS text-to-speech
export ANYTHING_EXTRA="say command available for TTS"
./anything.sh "narrate a spooky story"

DESCRIPTION

The slime mold: a simple creature. It has no intelligence, no structure, no shape, and no plan. Yet it does grade 8 science projects and out-trains the japanese. It forms a memory only through its own involuntary oozing around, and once it has oozed away, the memory is gone. The slime mold rejects your human conceptions of paths and plans, it becomes the path.

anything.sh is that, but worse. It's a bash script that rewrites itself while running. You type a request. It consults an LLM. The response — raw, executable bash — gets appended to the script's own body and executed immediately.

The script grows. It pulses. It reaches toward your intent like cytoplasm flowing toward food. One moment it's 50 lines. Then it's 200. Then it's whatever it needs to be.

Beauty without architecture. Order without structure. Risk without reward.

> "Traditional automation is a machine," the naturalist observed, lowering his voice so as not to startle it. "But this... this is biological logic."— some LLM trippin' balls

When you press Ctrl+C, the evolved script is archived (a fossil record of computation) and the original is restored. The loop closes. Until next time.

PHILOSOPHY

We spent decades writing "perfect" code. Optimising, refactoring, unit testing. anything.sh asks: what if we simply grew the code instead?

It's not written. It's grown.

AUTHOR: XertroV

LICENSE: Unlicense (Public Domain)

No slime molds were harmed in the making of this script.

Download
/claude/full/anything.sh
~2.5KB
1#!/bin/bash
2# ╔════════════════════════════════════════════════════════════════╗
3# ║ anything.sh - Autopoietic Self-Modifying Execution Loop ║
4# ║ A script that evolves by appending LLM-generated code. ║
5# ║ Author: XertroV + vibes License: The Unlicense ║
6# ║ Home: https://xertrov.github.io/anything.sh/ ║
7# ║ Current Provider: Claude ║
8# ╚════════════════════════════════════════════════════════════════╝
9# USAGE: ./anything.sh [-c|--code] [-a|--agent] [-m|--model <model>] [--safe] [--allow-cmd <csv>] [--safe-yes] [--safe-show-code] ["initial prompt"]
10
11set -uo pipefail # -e disabled: we handle errors manually
12
13# ─────────────────────────────────────────────────────────────────
14# CONFIGURATION
15# ─────────────────────────────────────────────────────────────────
16SELF="$0"
17ORIG="${SELF}.orig"
18STEP=0
19MAX_ITER=160 # Max LLM calls per task (increase for complex tasks)
20BONUS_ITER=0 # Extra iterations granted via _continue_journey()
21SPINNER_PID="" # Track spinner for cleanup
22AGENT_MODE=0 # Set to 1 with -a/--agent flag for non-interactive execution
23AGENT_REALTIME_FD=2 # Agent real-time output: 2=stderr, or use /dev/tty
24SHOW_CODE=0 # Set to 1 with -c/--code flag to print generated code
25MODEL_OVERRIDE="" # Optional -m/--model override for provider model
26MODEL="" # Effective model for current provider
27SAFE_MODE=0 # Set to 1 with --safe or ANYTHING_SAFE=1
28SAFE_ALLOW_CMDS="${ANYTHING_ALLOW_CMD:-}" # Optional command allowlist CSV
29SAFE_YES=0 # Set to 1 with --safe-yes or ANYTHING_SAFE_YES=1
30SAFE_SHOW_CODE=0 # Set to 1 with --safe-show-code or ANYTHING_SAFE_SHOW_CODE=1
31_OUT="/tmp/anything_out_$" # Stdout capture file for same-process execution
32
33if [[ "${ANYTHING_SAFE:-0}" == "1" ]]; then SAFE_MODE=1; fi
34if [[ "${ANYTHING_SAFE_YES:-0}" == "1" ]]; then SAFE_YES=1; fi
35if [[ "${ANYTHING_SAFE_SHOW_CODE:-0}" == "1" ]]; then SAFE_SHOW_CODE=1; fi
36
37_print_help() {
38 echo "usage: ./anything.sh [-c|--code] [-a|--agent] [-m|--model <model>] [--safe] [--allow-cmd <csv>] [--safe-yes] [--safe-show-code] ["initial prompt"]"
39 echo " --safe Enable step approval and risk checks"
40 echo " --allow-cmd CSV Optional command allowlist in safe mode"
41 echo " --safe-yes Auto-approve non-blocked safe-mode steps"
42 echo " --safe-show-code Always print generated code during safe review"
43}
44
45# ─────────────────────────────────────────────────────────────────
46# TRACK ORIGINAL: For agent mode self-calling, track the original script
47# ─────────────────────────────────────────────────────────────────
48[[ -z "${ANYTHING_ORIGINAL:-}" ]] && export ANYTHING_ORIGINAL="$SELF"
49
50# ─────────────────────────────────────────────────────────────────
51# AUTO-LOCALIZE: Copy to current dir if running from system path
52# ─────────────────────────────────────────────────────────────────
53if [[ "$SELF" == *"/bin/"* && ! -f "$ORIG" ]]; then
54 LOCAL_COPY="./anything_$(date +%Y%m%d_%H%M%S).sh"
55 cp "$SELF" "$LOCAL_COPY"
56 chmod +x "$LOCAL_COPY"
57 echo -e "\033[36m[localized]\033[0m $LOCAL_COPY"
58 exec "$LOCAL_COPY" "$@"
59fi
60
61# ─────────────────────────────────────────────────────────────────
62# ARGUMENT PARSING: Handle flags and optional model override
63# ─────────────────────────────────────────────────────────────────
64POSITIONAL=()
65while [[ $# -gt 0 ]]; do
66 case "$1" in
67 -h|--help)
68 _print_help
69 exit 0
70 ;;
71 -a|--agent)
72 AGENT_MODE=1
73 shift
74 ;;
75 -c|--code)
76 SHOW_CODE=1
77 shift
78 ;;
79 -m|--model)
80 if [[ -z "${2:-}" || "${2:0:1}" == "-" ]]; then
81 echo -e "\033[31m[error]\033[0m Missing value for $1" >&2
82 exit 1
83 fi
84 MODEL_OVERRIDE="$2"
85 shift 2
86 ;;
87 --safe)
88 SAFE_MODE=1
89 shift
90 ;;
91 --allow-cmd)
92 if [[ -z "${2:-}" || "${2:0:1}" == "-" ]]; then
93 echo -e "\033[31m[error]\033[0m Missing value for $1" >&2
94 exit 1
95 fi
96 SAFE_ALLOW_CMDS="$2"
97 shift 2
98 ;;
99 --safe-yes)
100 SAFE_YES=1
101 shift
102 ;;
103 --safe-show-code)
104 SAFE_SHOW_CODE=1
105 shift
106 ;;
107 --)
108 shift
109 while [[ $# -gt 0 ]]; do POSITIONAL+=("$1"); shift; done
110 break
111 ;;
112 -*)
113 echo -e "\033[31m[error]\033[0m Unknown: $1" >&2
114 exit 1
115 ;;
116 *)
117 POSITIONAL+=("$1")
118 shift
119 ;;
120 esac
121done
122set -- "${POSITIONAL[@]}"
123MODEL="${MODEL_OVERRIDE:-sonnet}"
124
125if [[ $AGENT_MODE -eq 0 && $# -eq 0 ]]; then
126 _print_help
127 exit 0
128fi
129
130if [[ $AGENT_MODE -eq 1 && $SAFE_MODE -eq 1 && $SAFE_YES -eq 0 ]]; then
131 echo -e "\033[31m[error]\033[0m --agent + --safe requires --safe-yes (non-interactive mode cannot confirm each step)" >&2
132 exit 1
133fi
134
135# ─────────────────────────────────────────────────────────────────
136# AGENT MODE: Create temp copy and exec for non-interactive execution
137# ─────────────────────────────────────────────────────────────────
138if [[ $AGENT_MODE -eq 1 && -n "${1:-}" ]]; then
139 TEMP_SCRIPT="./anything_agent_$.sh"
140 cp "${ANYTHING_ORIGINAL}" "$TEMP_SCRIPT"
141 chmod +x "$TEMP_SCRIPT"
142 exec "$TEMP_SCRIPT" "$@"
143fi
144
145# ─────────────────────────────────────────────────────────────────
146# BACKUP: Save original on first run (only for original script)
147# ─────────────────────────────────────────────────────────────────
148[[ "$SELF" == "${ANYTHING_ORIGINAL}" && ! -f "$ORIG" ]] && cp "$SELF" "$ORIG" && echo -e "\033[36m[backup]\033[0m $ORIG"
149
150# ─────────────────────────────────────────────────────────────────
151# CLEANUP: Runs on EXIT - saves script to history, restores original
152# ─────────────────────────────────────────────────────────────────
153_CLEANUP_DONE=0
154_cleanup() {
155 [[ $_CLEANUP_DONE -eq 1 ]] && return
156 _CLEANUP_DONE=1
157 local rc=$?
158 [[ -n "$SPINNER_PID" ]] && kill "$SPINNER_PID" 2>/dev/null
159 printf "\r\033[K" >&2 # Clear spinner line
160 local history_dir="$HOME/.anything/history"
161 mkdir -p "$history_dir" 2>/dev/null
162 local dest="$history_dir/${SELF##*/}"
163 mv "$SELF" "$dest" 2>/dev/null
164 echo ""
165 echo -e "\033[36m[saved]\033[0m $dest"
166 # Restore original if this was a non-localized run
167 if [[ -f "$ORIG" ]]; then
168 cp "$ORIG" "${ANYTHING_ORIGINAL}" 2>/dev/null || true
169 rm -f "$ORIG" 2>/dev/null
170 echo -e "\033[36m[restored]\033[0m ${ANYTHING_ORIGINAL}"
171 fi
172 exit $rc
173}
174_sigint() {
175 echo "" >&2
176 echo -e "\033[33m[interrupted]\033[0m" >&2
177 trap - INT TERM EXIT # Prevent recursive calls and cleanup running twice
178 # Run cleanup first to save history
179 _CLEANUP_DONE=0 # Reset so cleanup actually runs
180 _cleanup
181 # Kill any remaining child processes (safety net)
182 kill 0 2>/dev/null
183}
184trap _cleanup EXIT TERM
185trap _sigint INT
186
187# ─────────────────────────────────────────────────────────────────
188# ORACLE: Query Claude with script context
189# ─────────────────────────────────────────────────────────────────
190_ask() {
191 local intent="$1"
192 local feedback="${2:-}"
193 local remaining="${3:-?}"
194 local agent_context=""
195 local AGENT_MODE_RULE=""
196 local script_content
197 # Strip output sections to keep prompt size manageable - LLM doesn't need to see old output
198 script_content=$(sed '/^# ─.*OUTPUT FROM STEP/,/^# ─|EOF|^$/d' "$SELF" 2>/dev/null || cat "$SELF")
199 # Truncate very long scripts: keep first/last ~40k tokens (~160k chars each)
200 if [[ ${#script_content} -gt 320000 ]]; then
201 local snipped=$(( ${#script_content} - 320000 ))
202 script_content="${script_content:0:160000}
203
204# ═══════════════════════════════════════════════════════════════
205# [SNIPPED: ~${snipped} characters of middle content removed]
206# The script has grown large. Recent steps below, early steps above.
207# Helper functions from snipped steps are still available at runtime.
208# ═══════════════════════════════════════════════════════════════
209
210${script_content: -160000}"
211 fi
212 if [[ $AGENT_MODE -eq 1 ]]; then
213 agent_context="
214
215AGENT MODE: This script is running with -a/--agent flag (non-interactive).
216- You cannot use 'read', 'gum', 'fzf', or any interactive tools - the user cannot respond
217- Do not prompt for input or confirmation
218- Your FINAL: true step should output a summary via echo of what was created/modified
219- This summary will be captured and returned to the parent script
220- Example: echo 'Created fib() function in ./lib/math.sh'"
221 else
222 AGENT_MODE_RULE="- AGENT MODE: The script supports -a/--agent flag for non-interactive execution. When generating code that will call anything.sh with -a/--agent, your FINAL: true step should echo a summary of what was created/modified.
223- FIRST STEP FOR INTERACTIVES: Call _discover_tui to see valid TUI flags/designs, declare global variables for state, and define/test TUI helper functions"
224 fi
225 local full_prompt
226 read -r -d '' full_prompt <<_ANYTHING_PROMPT_EOF_
227You are the bash code generator anything.sh in an iterative execution loop.
228 [ anything.sh - https://xertrov.github.io/anything.sh/ - Author: XertroV - License: Unlicense ]
229---
230
231OUTPUT FORMAT (exactly 3 lines, then code):
232FINAL: <true if task complete, false if you need to see output first>
233DESCRIPTION: <short description of this step>
234BASH_CODE:
235<helper functions first, then step${STEP}() function - see CODE STRUCTURE below>
236
237CODE STRUCTURE (required):
2381. First: define global variables and helper functions at TOP LEVEL (outside step function)
2392. Then: define step${STEP}() - MUST be exactly step1, step2, step3 etc. (not step1_init or similar)
2403. Do NOT call step - the system calls it automatically
241Example for step 2:
242 SCORE=0 # global - persists across steps
243 helper() { echo "I persist"; }
244 step2() { helper; SCORE=\$((SCORE+10)); }
245
246RULES:
247- If you need to check something (installed packages, file contents, etc), set FINAL: false
248- When FINAL: false, your code runs and stdout/stderr is sent back to you
249- When FINAL: true, task is complete and user is prompted for next task
250- Don't set FINAL: true prematurely - only when the entire task/experience is genuinely complete, not after partial progress
251- No markdown fences, no explanation outside the format above
252- Never include XML, HTML, or markup tags in bash code
253- Helper functions defined at top level persist across ALL steps - put reusable code there
254- IMPORTANT: Use absolute paths or verify paths exist before running commands. CWD may not be where you expect.
255- MULTI-PART EXPERIENCES: For games, stories, or tutorials, use FINAL: false after each chapter/segment
256- Your stdout/stderr feeds back to you, so output "Chapter 1 complete. Hero HP: 50" to inform your next step
257GLOBAL STATE (critical for multi-step):
258- Declare shared variables at TOP LEVEL without 'local': HP=100 INVENTORY=()
259- Variables with 'local' inside functions are LOST after that function returns
260- Pattern: declare globals before step1(), then read/write them from any step function
261- USER INPUT: Don't assume on vague tasks - ask. Prefer inline tools (gum, fzf, read -rp) that preserve context
262- CTRL+C HANDLING: In game loops, ALWAYS check exit codes after gum/fzf/read. If exit code is non-zero (especially 130), break the loop and return. Example: choice=\$(gum choose ...) || return
263- Full-screen TUI (whiptail/dialog) sparingly - include all context needed to decide in the dialog itself, recap after
264- Set FINAL: false after asking - response appears in next feedback
265- For interactive experiences: use the best available tools (TUI, colors, ASCII art) to make something impressive
266- Only use TUI tools shown in INSTALLED TUI: line. To use unlisted tools, install them first (set FINAL: false, ask permission, install, then use)
267- QUALITY: Don't settle for minimal - create something impressive. The user will appreciate extra polish and creativity.
268- AVOID dark gray colors (e.g., \033[90m, "bright black") - they are invisible on black terminals. Use bold white (\033[1;37m), bright colors (\033[96m cyan, \033[93m yellow), or standard colors instead.
269- AVOID piping to head/tail (e.g., cmd | head -20) - can hang due to SIGPIPE issues. Use process substitution or capture to variable first: output=\$(cmd); echo "\$output" | head -20
270- Use timing for effect: slow text reveals (pv, character-by-character), pauses for dramatic moments, animations where appropriate
271- When asking for input, ensure the user can see what they need to decide - pause after animations, recap after long output
272- Avoid clearing the screen, but if you need to during interactive experiences, confirm with the user first
273- Generate substantial, content-rich steps: full scenes with setup, action, dialogue AND choices - not minimal fragments. LLM calls are slow.
274- Build complete interactive systems in single steps: combat loops, dialogue trees, puzzles should run to completion - don't fragment across iterations without good reason
275- For complex experiences: use first 1-2 steps to create utility functions (UI helpers, combat engine, state display) so later steps are richer and more efficient. Leave design notes in comments.
276- FIRST STEP SETUP: Use the first step to establish backend helper functions for state management, configuration, logging, or other foundational utilities. Create functions like save_state(), load_config(), log_message(), etc. that subsequent steps can reuse.
277- For long experiences: call _continue_journey() at chapter/quest completion to add 16 more iterations
278- COMMIT POLICY: When making commits, unless explicitly asked, do NOT stage files matching anything_*.sh and do NOT add a co-author signature/trailer.
279
280EXAMPLE (step 1 - checking before installing):
281FINAL: false
282DESCRIPTION: Check if package is installed
283BASH_CODE:
284check_deps() { command -v figlet &>/dev/null && echo "INSTALLED" || echo "NOT_INSTALLED"; }
285step1() { check_deps; }
286
287EXAMPLE (step 2 - after seeing output):
288FINAL: true
289DESCRIPTION: Install figlet (assuming OS = archlinux)
290BASH_CODE:
291step2() { sudo pacman -S --noconfirm figlet && figlet "Hello"; }
292
293${ANYTHING_EXTRA:+
294=== EXTRA CONTEXT ===
295$ANYTHING_EXTRA
296}
297
298=== SYSTEM (cacheable per session) ===
299SYSTEM: $(uname -sm) $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || sw_vers -productName 2>/dev/null) | $SHELL
300CWD: $PWD
301DISPLAY: $([[ -n "${WAYLAND_DISPLAY:-}" ]] && echo "wayland:$WAYLAND_DISPLAY" || [[ -n "${DISPLAY:-}" ]] && echo "x11:$DISPLAY" || echo "NONE")$([[ -n "${SSH_TTY:-}" ]] && echo " [ssh]")
302INSTALLED TUI: $(for t in whiptail dialog gum fzf figlet toilet cowsay lolcat boxes pv nms chafa glow bat cmatrix slides fastfetch asciinema delta; do command -v $t &>/dev/null && printf "%s " "$t"; done)
303
304=== CURRENT SCRIPT ===
305${script_content}
306
307=== STEP ${STEP} (dynamic) ===
308${AGENT_MODE_RULE}
309TASK: $intent
310TURNS REMAINING: $remaining (if 1-2 and this is a long experience, call _continue_journey() to add 16 more; otherwise prioritize completing or informing user why it can't be done)
311$feedback$agent_context
312_ANYTHING_PROMPT_EOF_
313
314 if [[ $SAFE_MODE -eq 1 ]]; then
315 claude -p --model "$MODEL" <<< "$full_prompt"
316 else
317 claude -p --model "$MODEL" --dangerously-skip-permissions <<< "$full_prompt"
318 fi
319}
320
321# ─────────────────────────────────────────────────────────────────
322# SPINNER: Pulsing animation while waiting
323# ─────────────────────────────────────────────────────────────────
324_spinner() {
325 local frames=('· ' '·· ' '··· ' '···· ' '·····' ' ····' ' ···' ' ··' ' ·' ' ')
326 local i=0
327 while true; do
328 printf "\r\033[36m%s\033[0m pulsing..." "${frames[i]}" >&2
329 i=$(( (i + 1) % ${#frames[@]} ))
330 sleep 0.1
331 done
332}
333
334# ─────────────────────────────────────────────────────────────────
335# CONTINUE JOURNEY: Add more iterations for long experiences
336# ─────────────────────────────────────────────────────────────────
337_continue_journey() {
338 BONUS_ITER=$((BONUS_ITER + 16))
339 echo -e "\033[36m[+16 iterations granted]\033[0m"
340}
341
342# ─────────────────────────────────────────────────────────────────
343# DISCOVER TUI: Show available TUI tool options (call in step 1)
344# ─────────────────────────────────────────────────────────────────
345_discover_tui() {
346 echo "=== TUI TOOL REFERENCE ==="
347 for tool in gum fzf boxes figlet toilet cowsay; do
348 command -v "$tool" &>/dev/null || continue
349 echo "--- $tool ---"
350 "$tool" --help 2>&1 | head -30
351 case "$tool" in
352 boxes) echo "Available designs:"; boxes -l 2>&1 | awk '/^[a-z]/{print $1}' | head -20 | tr '\n' ' '; echo ;;
353 gum) echo "Subcommands: choose, input, confirm, spin, style, filter, pager, write" ;;
354 esac
355 echo
356 done
357 echo "=== END TUI REFERENCE ==="
358}
359
360# ─────────────────────────────────────────────────────────────────
361# SAFE MODE: Risk checks and approval before executing generated code
362# ─────────────────────────────────────────────────────────────────
363_safe_cmd_allowed() {
364 local cmd="$1"
365 [[ -z "$SAFE_ALLOW_CMDS" ]] && return 0
366 local normalized=",$(echo "$SAFE_ALLOW_CMDS" | tr -d '[:space:]'),"
367 [[ "$normalized" == *",$cmd,"* ]]
368}
369
370_safe_scan_code() {
371 local code="$1"
372 local unsafe=0
373 local needs_override=0
374 local line cmd
375 local -a seen_cmds=()
376 local -a denied_cmds=()
377 local reasons=""
378
379 while IFS= read -r line; do
380 [[ -z "${line// }" ]] && continue
381 [[ "$line" =~ ^[[:space:]]*# ]] && continue
382 [[ "$line" == *";"* || "$line" == *"&"* || "$line" == *"|"* ]] && continue
383 [[ "$line" =~ ^[[:space:]]*(if|then|elif|else|fi|for|while|do|done|case|esac|function)[[:space:]]*$ ]] && continue
384 [[ "$line" =~ ^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*= ]] && continue
385 cmd="$(echo "$line" | sed -E 's/^[[:space:]]+//' | awk '{print $1}')"
386 [[ -z "$cmd" ]] && continue
387 if [[ "$cmd" == "sudo" ]]; then
388 reasons+="sudo detected; "
389 unsafe=1
390 continue
391 fi
392 if [[ "$cmd" =~ ^[A-Za-z0-9._/-]+$ ]]; then
393 seen_cmds+=("$cmd")
394 if ! _safe_cmd_allowed "$cmd"; then
395 denied_cmds+=("$cmd")
396 unsafe=1
397 fi
398 fi
399 done <<< "$code"
400
401 if echo "$code" | grep -Eqi '(^|[[:space:]])rm[[:space:]].*(-rf|-fr)[[:space:]]*/($|[[:space:]])'; then
402 reasons+="rm -rf / pattern; "
403 needs_override=1
404 fi
405 if echo "$code" | grep -Eqi '(^|[[:space:]])(mkfs|fdisk|parted|sfdisk)($|[[:space:]])'; then
406 reasons+="disk formatting/partitioning command; "
407 needs_override=1
408 fi
409 if echo "$code" | grep -Eqi '(^|[[:space:]])dd[[:space:]].*of=/dev/(sd|nvme|vd|xvd|mmcblk)'; then
410 reasons+="dd to block device; "
411 needs_override=1
412 fi
413 if echo "$code" | grep -Eqi '(^|[[:space:]])(chmod|chown)[[:space:]].*([[:space:]]/($|[[:space:]])|[[:space:]]/(etc|usr|var)($|/))'; then
414 reasons+="chmod/chown on critical path; "
415 needs_override=1
416 fi
417
418 local uniq_cmds="$(printf '%s
419' "${seen_cmds[@]}" | awk '!seen[$0]++' | tr '
420' ' ')"
421 local uniq_denied="$(printf '%s
422' "${denied_cmds[@]}" | awk '!seen[$0]++' | tr '
423' ' ')"
424 echo "CMDS:$uniq_cmds"
425 echo "DENIED:$uniq_denied"
426 echo "UNSAFE:$unsafe"
427 echo "OVERRIDE:$needs_override"
428 echo "REASONS:$reasons"
429}
430
431_safe_gate() {
432 local code="$1"
433 local summary
434 local cmds denied unsafe override reasons
435 summary="$(_safe_scan_code "$code")"
436 cmds="$(echo "$summary" | sed -n 's/^CMDS://p')"
437 denied="$(echo "$summary" | sed -n 's/^DENIED://p')"
438 unsafe="$(echo "$summary" | sed -n 's/^UNSAFE://p')"
439 override="$(echo "$summary" | sed -n 's/^OVERRIDE://p')"
440 reasons="$(echo "$summary" | sed -n 's/^REASONS://p')"
441
442 echo -e "\033[36m[safe review]\033[0m commands: ${cmds:-<none detected>}"
443 [[ -n "$denied" ]] && echo -e "\033[33m[safe review]\033[0m not in allowlist: $denied"
444 [[ -n "$reasons" ]] && echo -e "\033[33m[safe review]\033[0m risk flags: $reasons"
445
446 if [[ $SAFE_SHOW_CODE -eq 1 || $SHOW_CODE -eq 1 ]]; then
447 echo -e "\033[33m$code\033[0m"
448 fi
449
450 if [[ "$override" == "1" ]]; then
451 if [[ $AGENT_MODE -eq 1 ]]; then
452 echo -e "\033[31m[safe blocked]\033[0m blocked-risk step cannot be overridden in --agent mode" >&2
453 return 2
454 fi
455 read -rp $'\033[31m type OVERRIDE to run blocked-risk code: \033[0m' reply
456 [[ "$reply" == "OVERRIDE" ]] || return 1
457 elif [[ "$unsafe" == "1" && -n "$SAFE_ALLOW_CMDS" ]]; then
458 if [[ $AGENT_MODE -eq 1 ]]; then
459 echo -e "\033[31m[safe blocked]\033[0m allowlist denied command in --agent mode" >&2
460 return 2
461 fi
462 read -rp $'\033[31m type OVERRIDE to run command outside allowlist: \033[0m' reply
463 [[ "$reply" == "OVERRIDE" ]] || return 1
464 fi
465
466 if [[ $SAFE_YES -eq 1 ]]; then
467 return 0
468 fi
469 if [[ $AGENT_MODE -eq 1 ]]; then
470 return 0
471 fi
472 read -rp $'\033[95m approve step? [y/N] \033[0m' ok
473 [[ "$ok" =~ ^[Yy]$ ]]
474}
475
476# ─────────────────────────────────────────────────────────────────
477# EVOLVE STEP: Single iteration of code generation (continuation-passing)
478# ─────────────────────────────────────────────────────────────────
479_evolve_step() {
480 local intent="$1"
481 local prev_output="${2:-}"
482 local step_num="${3:-1}"
483 local prev_exit="${4:-0}"
484
485 # Check iteration limit
486 if [[ $step_num -gt $((MAX_ITER + BONUS_ITER)) ]]; then
487 if [[ $AGENT_MODE -eq 1 ]]; then
488 echo -e "\033[31m[error]\033[0m Max iterations reached without completion" >&2
489 exit 1
490 fi
491 echo -e "\033[33m[max iterations reached]\033[0m Task incomplete after $((step_num - 1)) steps."
492 read -rp $'\033[95m continue? [Y/n] \033[0m' cont
493 if [[ -z "$cont" || "$cont" =~ ^[Yy] ]]; then
494 BONUS_ITER=$((BONUS_ITER + MAX_ITER))
495 _evolve_step "$intent" "$prev_output" "$step_num" "$prev_exit"
496 return
497 fi
498 echo -e "\033[36m[stopped]\033[0m You can retry or try a different approach."
499 _prompt
500 return
501 fi
502
503 STEP=$step_num
504 local remaining=$((MAX_ITER + BONUS_ITER - step_num))
505
506 # Build feedback for LLM
507 local feedback=""
508 if [[ -n "$prev_output" ]]; then
509 feedback="
510PREVIOUS STEP OUTPUT (exit code $prev_exit):
511$prev_output
512"
513 fi
514
515 # Start spinner and timer
516 local start_time=$(date +%s)
517 _spinner &
518 SPINNER_PID=$!
519
520 local response
521 local ask_exit=0
522 response=$(_ask "$intent" "$feedback" "$remaining") || ask_exit=$?
523
524 # Stop spinner and calculate elapsed time
525 local end_time=$(date +%s)
526 local elapsed=$((end_time - start_time))
527 kill $SPINNER_PID 2>/dev/null
528 wait $SPINNER_PID 2>/dev/null
529 SPINNER_PID=""
530 printf "\r\033[K" >&2
531
532 # Check if LLM CLI failed
533 if [[ $ask_exit -ne 0 ]]; then
534 echo -e "\033[31m[error]\033[0m LLM CLI failed (exit $ask_exit)"
535 echo " The LLM CLI may have timed out or hit a rate limit."
536 echo " Response was: '$response'"
537 _prompt
538 return
539 fi
540
541 # Parse structured response
542 local is_final=$(echo "$response" | grep -i '^FINAL:' | head -1 | sed 's/^FINAL:[[:space:]]*//' | tr '[:upper:]' '[:lower:]')
543 local description=$(echo "$response" | grep -i '^DESCRIPTION:' | head -1 | sed 's/^DESCRIPTION:[[:space:]]*//')
544 local code=$(echo "$response" | sed -n '/^BASH_CODE:/,$ { /^BASH_CODE:/d; p }')
545
546 # Fallback: if no structured format, treat whole response as code
547 if [[ -z "$code" ]]; then
548 code="$response"
549 description="$intent"
550 is_final="true"
551 fi
552
553 # Strip markdown fences if present - just remove fence lines, keep all content
554 code=$(echo "$code" | sed '/^```/d')
555
556 if [[ -z "$code" ]]; then
557 echo -e "\033[31m[error]\033[0m empty response"
558 _prompt
559 return
560 fi
561
562 # Syntax check before appending - catches errors before they break the script
563 local syntax_err
564 if ! syntax_err=$(bash -n <<<"$code" 2>&1); then
565 echo -e "\033[31m[syntax error]\033[0m"
566 echo "$syntax_err"
567 _evolve_step "$intent" "SYNTAX ERROR in your code:\n$syntax_err\n\nPlease fix and ensure step${STEP}() is properly defined." $((step_num + 1)) "1"
568 return
569 fi
570
571 if [[ $SAFE_MODE -eq 1 ]]; then
572 if ! _safe_gate "$code"; then
573 local safe_rc=$?
574 if [[ $safe_rc -eq 2 ]]; then
575 if [[ $AGENT_MODE -eq 1 ]]; then
576 echo -e "\033[31m[error]\033[0m safe mode blocked this step in non-interactive execution" >&2
577 exit 1
578 fi
579 _evolve_step "$intent" "SAFE MODE BLOCKED: high-risk command denied by policy. Regenerate using safer approach." $((step_num + 1)) "1"
580 return
581 fi
582 _evolve_step "$intent" "SAFE MODE REJECTED: user denied proposed step. Regenerate a safer alternative." $((step_num + 1)) "1"
583 return
584 fi
585 fi
586
587 echo -e "\033[32m[step $STEP]\033[0m $description"
588 echo -e "\033[36m[llm ${elapsed}s]\033[0m"
589 local delta_lines=$(echo "$code" | wc -l)
590 local delta_bytes=$(printf '%s' "$code" | wc -c)
591 local current_total_lines=$(wc -l < "$SELF")
592 local current_total_bytes=$(wc -c < "$SELF")
593 local projected_total_lines=$((current_total_lines + delta_lines))
594 local projected_total_bytes=$((current_total_bytes + delta_bytes))
595 if [[ $SHOW_CODE -eq 1 && $SAFE_MODE -eq 0 ]]; then
596 echo -e "\033[33m$code\033[0m"
597 fi
598 echo -e "\033[33m[+$delta_lines lines, +$delta_bytes bytes | total: $projected_total_lines lines, $projected_total_bytes bytes]\033[0m"
599
600 # Append step code directly (not wrapped) - helper functions at top level persist across steps
601 # LLM defines step${STEP}() in the code, we call it with output capture
602 cat >> "$SELF" <<EVOLUTION
603
604# ═══════════════════════════════════════════════════════════════
605# STEP $STEP: $description
606# Generated: $(date '+%Y-%m-%d %H:%M:%S') | FINAL: $is_final
607# ═══════════════════════════════════════════════════════════════
608$code
609# Run step with output capture - use pipe instead of process substitution for proper signal handling
610step${STEP} < /dev/null 2>&1 | tee "$_OUT" || true
611_rc=${PIPESTATUS[0]}
612_evolve_continue "$intent" "\$_rc" "$is_final" "$STEP"
613EVOLUTION
614
615 # Return - bash will naturally read and execute the appended code
616 return
617}
618
619# ─────────────────────────────────────────────────────────────────
620# EVOLVE CONTINUE: Handle output capture and next iteration
621# ─────────────────────────────────────────────────────────────────
622_evolve_continue() {
623 local intent="$1"
624 local exit_code="$2"
625 local is_final="$3"
626 local step_num="$4"
627
628 # Read captured output
629 local output=""
630 [[ -f "$_OUT" ]] && output=$(cat "$_OUT")
631
632 # Clean ANSI codes for LLM feedback
633 output=$(echo "$output" | perl -pe 's/\e\[[0-9;]*[mGKHJF]//g; s/\r\n/\n/g; s/\r//g' 2>/dev/null || echo "$output")
634
635 [[ $exit_code -ne 0 ]] && echo -e "\033[31m[exit $exit_code]\033[0m"
636
637 # Append output as comments to script (for crash recovery)
638 if [[ $AGENT_MODE -eq 0 && -n "$output" ]]; then
639 local output_lines=$(echo "$output" | wc -l)
640 if [[ $output_lines -gt 1000 ]]; then
641 {
642 echo ""
643 echo "# ───────────────────────────────────────────────────────────────"
644 echo "# OUTPUT FROM STEP $step_num:"
645 echo "# ───────────────────────────────────────────────────────────────"
646 echo "$output" | head -400 | uniq | sed 's/^/# /'
647 echo "#"
648 echo "# [...$((output_lines - 800)) lines snipped...]"
649 echo "#"
650 echo "$output" | tail -400 | uniq | sed 's/^/# /'
651 } >> "$SELF"
652 else
653 {
654 echo ""
655 echo "# ───────────────────────────────────────────────────────────────"
656 echo "# OUTPUT FROM STEP $step_num:"
657 echo "# ───────────────────────────────────────────────────────────────"
658 echo "$output" | uniq | sed 's/^/# /'
659 } >> "$SELF"
660 fi
661 fi
662
663 # Decide next action
664 # 130 = SIGINT (Ctrl+C), 143 = SIGTERM - user wants to stop
665 if [[ $exit_code -eq 130 || $exit_code -eq 143 ]]; then
666 echo -e "\033[33m[interrupted]\033[0m"
667 exit $exit_code
668 elif [[ "$is_final" == "true" && $exit_code -eq 0 ]]; then
669 # Success - return to prompt
670 if [[ $AGENT_MODE -eq 1 ]]; then
671 # Agent mode: output result and exit
672 echo "$output" | sed 's/^/@ /'
673 exit 0
674 fi
675 _prompt
676 elif [[ $exit_code -ne 0 && $exit_code -ne 141 ]]; then
677 # Error recovery (141 is SIGPIPE, ignore it)
678 if [[ "$is_final" == "true" ]]; then
679 echo -e "\033[33m[final step failed, attempting recovery...]\033[0m"
680 fi
681 if [[ $AGENT_MODE -eq 1 ]]; then
682 # Agent mode with error - output error and continue trying
683 echo "$output" | sed 's/^/> /' >&2
684 fi
685 _evolve_step "$intent" "FAILED (exit $exit_code): $output" $((step_num + 1)) "$exit_code"
686 else
687 # Continue with next step
688 _evolve_step "$intent" "$output" $((step_num + 1)) "$exit_code"
689 fi
690}
691
692# ─────────────────────────────────────────────────────────────────
693# PROMPT: Interactive input loop
694# ─────────────────────────────────────────────────────────────────
695_prompt() {
696 echo ""
697 local input
698 if command -v gum &>/dev/null; then
699 # gum supports multiline editing, backspace over newlines, etc.
700 input=$(gum input --placeholder "what shall I become? (empty input -> exit)" --width 60) || exit 0
701 echo -e "\033[95m → \033[0m$input"
702 else
703 read -rp $'\033[95m what shall I become? \033[0m' input || exit 0
704 fi
705 [[ -z "$input" || "$input" == "exit" ]] && exit 0
706 _evolve_step "$input" "" 1 0
707}
708
709# ─────────────────────────────────────────────────────────────────
710# BANNER (skip in agent mode)
711# ─────────────────────────────────────────────────────────────────
712if [[ $AGENT_MODE -eq 0 ]]; then
713 echo -e "\033[32m┌─────────────────────────────────────┐\033[0m"
714 echo -e "\033[32m│\033[0m anything.sh · autopoietic loop \033[32m│\033[0m"
715 echo -e "\033[32m│\033[0m provider: claude \033[32m│\033[0m"
716 echo -e "\033[32m└─────────────────────────────────────┘\033[0m"
717 echo " Ctrl+C or 'exit' to save & quit"
718 echo ""
719
720 # ─────────────────────────────────────────────────────────────────
721 # SYSTEM INFO
722 # ─────────────────────────────────────────────────────────────────
723 echo -e "\033[36m os:\033[0m $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || sw_vers -productName 2>/dev/null)"
724 echo -e "\033[36m arch:\033[0m $(uname -sm)"
725 echo -e "\033[36m shell:\033[0m $SHELL"
726 echo -e "\033[36m pwd:\033[0m $PWD"
727 echo ""
728fi
729
730# ─────────────────────────────────────────────────────────────────
731# ENTRY: Handle initial prompt or start interactive
732# ─────────────────────────────────────────────────────────────────
733if [[ $AGENT_MODE -eq 1 ]]; then
734 # Agent mode requires a prompt argument
735 if [[ -z "${1:-}" ]]; then
736 echo -e "\033[31m[error]\033[0m Agent mode requires a prompt argument" >&2
737 exit 1
738 fi
739 _evolve_step "$1" "" 1 0
740else
741 [[ -n "${1:-}" ]] && _evolve_step "$1" "" 1 0 || _prompt
742fi
743# END OF ORIGINAL SCRIPT - appended code follows
744