From 12897ccfc25cc7d11f4778160b718df3673c8ac9 Mon Sep 17 00:00:00 2001 From: satyaborg Date: Mon, 1 Jun 2026 16:39:40 +1000 Subject: [PATCH] feat: add compact status spinners --- devloop | 120 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 19 deletions(-) diff --git a/devloop b/devloop index 1923f0d..4071dd0 100755 --- a/devloop +++ b/devloop @@ -44,6 +44,8 @@ RUN_CODE=0 RUN_STDOUT="" RUN_STDERR="" RUN_OUTPUT="" +RUN_WAIT_STYLE="timed" +RUN_WAIT_LABEL="working" RUN_TIMEOUT_MINUTES="$DEFAULT_TIMEOUT_MINUTES" RUN_TIMEOUT_SECONDS=$((DEFAULT_TIMEOUT_MINUTES * 60)) RUN_DEADLINE=0 @@ -964,6 +966,42 @@ ui_spinner_wait() { printf '\r\033[K' >&2 } +ui_compact_spinner_wait() { + local pid="$1" + local done_file="$2" + local stderr_file="${3:-}" + local label="${4:-working}" + local index=0 frame + local frames=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏") + if [ "$USE_TUI" != true ] || [ ! -t 2 ]; then + while [ ! -s "$done_file" ] && kill -0 "$pid" 2>/dev/null; do + if run_deadline_reached; then + RUN_TIMED_OUT=true + if [ -n "$stderr_file" ]; then timeout_message >> "$stderr_file"; fi + printf '%s\n' "124" > "$done_file" + terminate_pid_tree "$pid" + return + fi + sleep 0.12 + done + return + fi + while [ ! -s "$done_file" ] && kill -0 "$pid" 2>/dev/null; do + if run_deadline_reached; then + RUN_TIMED_OUT=true + if [ -n "$stderr_file" ]; then timeout_message >> "$stderr_file"; fi + printf '%s\n' "124" > "$done_file" + terminate_pid_tree "$pid" + return + fi + frame="${frames[$((index % ${#frames[@]}))]}" + printf '\r\033[K%s %s' "$(ui_color rec "$frame")" "$label" >&2 + sleep 0.12 + index=$((index + 1)) + done + printf '\r\033[K' >&2 +} + event_store_start() { local id="$1" local title="$2" @@ -1954,12 +1992,11 @@ run_devloop() { return 2 fi - event_step "naming" "derive branch name with $(agent_label "$coder")" local naming_error naming_error="" if ! resolve_work_item "$coder" "$SOURCE_REPO" "$spec" "$spec_text"; then naming_error="$WORK_ITEM_ERROR" - event_done "naming" false "$naming_error" + event_gate "naming" 0 "$naming_error" if [ -n "${WORK_ITEM_LOG:-}" ]; then printf 'naming failed: %s\nnaming log: %s\n' "$naming_error" "$WORK_ITEM_LOG" >&2 else @@ -1969,7 +2006,7 @@ run_devloop() { return 2 fi local slug="$WORK_SLUG" - event_done "naming" true "$(branch_base "$WORK_TYPE" "$WORK_BREAKING" "$WORK_SLUG")" + event_gate "naming" 1 "$(branch_base "$WORK_TYPE" "$WORK_BREAKING" "$WORK_SLUG")" local repo="$SOURCE_REPO" if [ "$use_worktree" = true ]; then @@ -2131,12 +2168,11 @@ run_devloop() { if [ "$PASSES" -gt "$max" ]; then PASSES="$max"; fi if [ "$create_pr" = true ] && [ "$STATUS" = "accepted" ]; then - event_step "pull-request" "push branch and create PR" if create_pull_request "$repo" "$FINAL_BRANCH" "$base"; then - event_done "pull-request" true "${PULL_REQUEST:-origin/$FINAL_BRANCH}" + event_gate "pull request" 1 "${PULL_REQUEST:-origin/$FINAL_BRANCH}" else STATUS="pr-error" - event_done "pull-request" false "$PULL_REQUEST_ERROR" + event_gate "pull request" 0 "$PULL_REQUEST_ERROR" fi fi @@ -2584,7 +2620,7 @@ resolve_work_item() { log="$tmpdir/naming.log" WORK_ITEM_LOG="$log" prompt="$(naming_prompt "$spec" "$spec_text")" - if ! run_agent_once "$agent" "$repo" "$log" "$prompt" "naming"; then + if ! with_compact_wait "name work" run_agent_once "$agent" "$repo" "$log" "$prompt" ""; then WORK_ITEM_ERROR="${RUN_OUTPUT:-$(agent_label "$agent") failed}" return 1 fi @@ -2915,7 +2951,10 @@ run_with_prompt() { printf '%s\n' "$?" > "$code_file" ) & pid=$! - ui_spinner_wait "$pid" "$code_file" "${UI_STEP_TITLE:-working}" "$stderr_file" + case "$RUN_WAIT_STYLE" in + compact) ui_compact_spinner_wait "$pid" "$code_file" "$stderr_file" "$RUN_WAIT_LABEL" ;; + *) ui_spinner_wait "$pid" "$code_file" "${UI_STEP_TITLE:-working}" "$stderr_file" ;; + esac wait "$pid" >/dev/null 2>&1 || true if [ -f "$code_file" ]; then RUN_CODE="$(cat "$code_file")" @@ -2941,6 +2980,51 @@ run_with_prompt() { rm -f "$prompt_file" "$stdout_file" "$stderr_file" "$output_file" "$code_file" } +with_compact_wait() { + local label="$1" + shift + local old_style="$RUN_WAIT_STYLE" + local old_label="$RUN_WAIT_LABEL" + local code + RUN_WAIT_STYLE="compact" + RUN_WAIT_LABEL="$label" + "$@" + code=$? + RUN_WAIT_STYLE="$old_style" + RUN_WAIT_LABEL="$old_label" + return "$code" +} + +run_compact_command() { + local cwd="$1" + local label="$2" + shift 2 + local output_file code_file pid + output_file="$(mktemp "${TMPDIR:-/tmp}/devloop-command-output.XXXXXX")" + code_file="$(mktemp "${TMPDIR:-/tmp}/devloop-command-code.XXXXXX")" + rm -f "$code_file" + ( + cd "$cwd" >/dev/null 2>&1 && "$@" > "$output_file" 2>&1 + printf '%s\n' "$?" > "$code_file" + ) & + pid=$! + ui_compact_spinner_wait "$pid" "$code_file" "$output_file" "$label" + wait "$pid" >/dev/null 2>&1 || true + if [ -f "$code_file" ]; then + RUN_CODE="$(cat "$code_file")" + else + RUN_CODE=1 + fi + case "$RUN_CODE" in + ''|*[!0-9]*) RUN_CODE=1 ;; + esac + RUN_OUTPUT="$(cat "$output_file")" + RUN_STDOUT="$RUN_OUTPUT" + RUN_STDERR="" + rm -f "$output_file" "$code_file" + [ "$RUN_CODE" -eq 0 ] +} + terminate_pid_tree() { local pid="$1" # This catches the shell and direct children portably on macOS/Linux. @@ -3323,14 +3407,17 @@ create_pull_request() { local base="$3" local remote="origin" local out - if ! out="$(git -C "$repo" push -u "$remote" "$branch" 2>&1)"; then + if ! run_compact_command "$repo" "push branch" git push -u "$remote" "$branch"; then + out="$RUN_OUTPUT" PULL_REQUEST_ERROR="$(printf '%s\n' "$out" | sed '/^[[:space:]]*$/d' | tail -n 1)" return 1 fi - if ! out="$(cd "$repo" >/dev/null 2>&1 && gh pr create --fill --base "$base" --head "$branch" 2>&1)"; then + if ! run_compact_command "$repo" "open pull request" gh pr create --fill --base "$base" --head "$branch"; then + out="$RUN_OUTPUT" PULL_REQUEST_ERROR="pushed $remote/$branch, but PR creation failed: $(printf '%s\n' "$out" | sed '/^[[:space:]]*$/d' | tail -n 1)" return 1 fi + out="$RUN_OUTPUT" PULL_REQUEST="$(printf '%s\n' "$out" | awk '/^https?:\/\// { print; exit }')" if [ -z "$PULL_REQUEST" ]; then PULL_REQUEST="$(printf '%s\n' "$out" | tail -n 1)"; fi } @@ -3404,7 +3491,7 @@ Style: - No emoji. EOF )" - run_agent "$reviewer" "$repo" "$reviewer_session_file" "$repo/.codex/logs/$slug-report.log" "$prompt" "report" >/dev/null 2>&1 || true + with_compact_wait "write report" run_agent "$reviewer" "$repo" "$reviewer_session_file" "$repo/.codex/logs/$slug-report.log" "$prompt" "" || true } report_metadata() { @@ -3607,35 +3694,30 @@ spec_command() { today="$(date +%F)" spec_dir="$(devloop_spec_dir)" prompt="$(spec_prompt "$context" "$output" "$skill" "$today" "$spec_dir")" - if [ "$USE_TUI" = true ]; then event_step "spec" "generate spec with $(agent_label "$agent")"; fi if [ "$agent" = "codex" ]; then - run_with_prompt "$PWD" "" "" "$prompt" codex exec "${CODEX_MODEL_ARGS[@]}" "${CODEX_REASONING_ARGS[@]}" -s read-only -C "$PWD" - + with_compact_wait "generate spec" run_with_prompt "$PWD" "" "" "$prompt" codex exec "${CODEX_MODEL_ARGS[@]}" "${CODEX_REASONING_ARGS[@]}" -s read-only -C "$PWD" - elif [ "$agent" = "claude" ]; then - run_with_prompt "$PWD" "" "" "$prompt" claude -p "${CLAUDE_MODEL_ARGS[@]}" "${CLAUDE_EFFORT_ARGS[@]}" --add-dir "$PWD" + with_compact_wait "generate spec" run_with_prompt "$PWD" "" "" "$prompt" claude -p "${CLAUDE_MODEL_ARGS[@]}" "${CLAUDE_EFFORT_ARGS[@]}" --add-dir "$PWD" else - run_with_prompt "$PWD" "" "" "$prompt" "$agent" + with_compact_wait "generate spec" run_with_prompt "$PWD" "" "" "$prompt" "$agent" fi if [ "$RUN_CODE" -ne 0 ]; then - if [ "$USE_TUI" = true ]; then event_done "spec" false "failed"; fi if [ -n "$RUN_STDERR" ]; then printf '%s\n' "$RUN_STDERR" >&2; else printf '%s\n' "${RUN_STDOUT:-spec agent failed}" >&2; fi return 2 fi local markdown file if ! markdown="$(extract_generated_spec "$RUN_STDOUT$RUN_STDERR")"; then - if [ "$USE_TUI" = true ]; then event_done "spec" false "invalid output"; fi printf '%s\n' "agent output must include spec frontmatter" >&2 return 2 fi file="$(generated_spec_path "$markdown" "$output" "$today" "$force")" if [ -f "$file" ] && [ "$force" = false ]; then - if [ "$USE_TUI" = true ]; then event_done "spec" false "already exists"; fi printf 'spec already exists: %s\n' "$file" >&2 return 2 fi mkdir -p "$(dirname "$file")" printf '%s\n' "$markdown" > "$file" - if [ "$USE_TUI" = true ]; then event_done "spec" true "$file"; fi printf 'spec: %s\n' "$file" printf 'agent: %s\n' "$(agent_label "$agent")" }