Releases: shaifulshabuj/devloop
DevLoop v5.1.8 — Bash 3.2 empty-array fix
Bug Fix: Bash 3.2 empty-array runtime error
Problem
On macOS (which ships bash 3.2 as the system shell), iterating over an empty array with "${arr[@]}" and set -u active triggers an "unbound variable" error at runtime. This caused the "line 9712: syntax error near unexpected token `)'" error seen after v5.1.7.
The affected array (fix_history_parts) is empty when devloop resume runs its re-architect escalation path before any fix rounds have been recorded in the current session.
Fix
Replaced all 4 fix_history_parts[@] iterations with the bash 3.2-safe pattern:
for h in "${fix_history_parts[@]+"${fix_history_parts[@]}"}" ; do
...
doneThis expands to nothing on empty arrays without triggering set -u.
Affected commands
devloop run— fix escalation respec pathdevloop resume— fix escalation respec path + deep-fix history path
Update
devloop updatev5.1.7 — Fix resume re-architect escalation
What's Changed
Bug Fix
devloop resume now correctly escalates to re-architect after max fix retries are exhausted.
Previously, when a session was resumed that had already hit the max retry limit (3), cmd_resume's reviewer+fix loop would run one more fix round and then exit with "Max fix retries reached" — without triggering the re-architect phase (_run_respec_phase) that devloop run uses.
Root cause: The NEEDS_WORK branch in cmd_resume was missing the full escalation block present in cmd_run.
Changes:
cmd_resumeNEEDS_WORK branch now mirrorscmd_runexactly: calls_run_respec_phasewhenDEVLOOP_FIX_STRATEGY=escalate(default)- Added
--no-respecflag todevloop resumefor parity withdevloop run --no-respec - Added human-checkpoint inbox write before the last fix round before respec
Upgrade
devloop updatev5.1.6 — Review hardening: huge diffs & provider errors
What's New
cmd_review now handles oversized diffs and provider errors gracefully
Triggered by a real failure: An Electron project with an untracked out/ directory produced a 734 KB diff. Claude replied Prompt is too long, the engine couldn't parse a verdict, and eventually aborted with Unknown verdict repeated — stopping.
Three new guards
1. Pathspec exclusion — build/dep dirs dropped from diff by default:
Default excludes: out dist build .next .turbo coverage node_modules *.min.js *.bundle.js *.map
Override with DEVLOOP_REVIEW_EXCLUDE="<space-separated pathspecs>"
Restore legacy behaviour: DEVLOOP_REVIEW_EXCLUDE=none
2. Byte-size cap — diff truncated at 150 KB by default:
- Controlled by
DEVLOOP_REVIEW_MAX_BYTES(default150000) - Set to
0to disable - Truncated diffs get a clear marker so the reviewer knows
3. Provider-error detector — detects failure replies before the retry loop:
- Recognises:
Prompt is too long,rate_limit,context length exceeded,overloaded_error,Request timed out, etc. - Exits with code 4 (
provider-errorstatus) + actionable hints - No longer falls through to the generic
Unknown verdictretry loop
How to update
devloop updatev5.1.5 — Fix arithmetic crash in reviewer/fix loop
What's Fixed
((: 0\n0: syntax error in expression crash
Symptom: /usr/local/bin/devloop: line 8704: ((: 0 0: syntax error in expression (error token is "0")
Root cause: set -o pipefail + || echo 0 fallback pattern produces double output:
# BROKEN: with pipefail, when grep finds no matches:
# 1. grep exits 1 → pipefail propagates
# 2. wc -l still outputs "0"
# 3. || echo 0 ALSO fires → output is "0\n0"
count="$(grep ... | grep "fix-" | wc -l | tr -d ' ' || echo 0)"
# then: (( count == 0 )) → crash on embedded newlineFix: Use grep -c || true — grep -c always outputs a clean integer (0 or N), and || true adds no output:
count="$(grep ... | grep -c "fix-" || true)"
count="${count:-0}"How to update
devloop updatev5.1.4 — Fix stale Live View status after resume
What's Fixed
Live View shows correct status after devloop resume
Bug: After a session timed out (timed-out-at-plan or timed-out-at-diff), running devloop resume would restart the pipeline — but the Live View panel kept showing the old status (e.g. timed-out-at-plan) even while the pipeline was actively running again.
Root cause: _session_finish writes the terminal status to /status. cmd_resume never reset this file before relaunching the pipeline.
Fix: cmd_resume now immediately:
- Writes
runningtostatus - Removes
finished_at
...before relaunching the pipeline, so the Live View immediately reflects the resumed state.
How to update
devloop updatev5.1.3 — Fix truncated download guard in devloop update
What's Fixed
devloop update now validates downloads before installing
Root cause of the unexpected EOF while looking for matching '"' errors after devloop update: the previous sanity check only verified the word devloop appeared in the file. A truncated download (partial network transfer) passes this check but results in a broken installation.
Fix: Three-stage validation before installing:
- File size > 50 KB — rejects partial/truncated downloads
VERSION=line present — confirms it's a devloop scriptbash -nsyntax check — catches any corrupted or truncated file before it overwrites your working installation
How to update
devloop updateOr manually:
curl -fsSL https://raw.githubusercontent.com/shaifulshabuj/devloop/main/devloop.sh -o /usr/local/bin/devloop && chmod +x /usr/local/bin/devloopv5.1.2 — bash version check in doctor
Fixed
devloop doctornow checks your bash version and warns when running on macOS system bash (3.2.x):
⚠ bash 3.2.57(1)-release — macOS system bash (3.2) works but upgrade recommended
→ brew install bash then add /opt/homebrew/bin to PATH
macOS ships bash 3.2 for licensing reasons. devloop works on bash 3.2 but bash 4+ is recommended.
v5.1.1 — Pipeline stuck-state recovery fixes
Fixed — pipeline stuck-state recovery
This patch release resolves the pipeline stuck-state bugs reported in issues #2, #3, and #4.
Approval gate timeout recovery
_approval_gatenow returns exit code 3 (stalled) vs 1 (active rejection)- New
timed-out-at-plan/timed-out-at-diffsession statuses — shown indevloop resume --list - Timeout leaves a recovery hint:
devloop resume <id>instead of a deadbash-3.2$prompt
devloop resume gate re-presentation
timed-out-at-diff/rejected-at-diff→ re-shows only the diff gate (worker doesn't re-run)timed-out-at-plan/rejected-at-plan→ re-shows plan gate for a fresh approvalrejected-at-planandrejected-at-diffare now resumable (previously treated as terminal)--approve-diffflag force-approves the diff gate when resuming a timed-out session
devloop continue alias
devloop continue <id>now correctly aliasesdevloop resume <id>- Was previously misrouted to the natural-language router, spawning a duplicate architect phase and Live View
Incomplete spec auto-retry
cmd_architectretries spec generation up to 2 times if## Copilot Instructions Blockis missingdevloop runauto-re-architects once if the first attempt exits non-zero_extract_plan_summaryappends a⚠ WARNINGat plan-gate time when block is missingcmd_workerror now shows exact recovery commands:devloop architect "<feature>"+devloop work <id>
Full changelog: CHANGELOG.md
v5.1.0 — TUI dashboard, chat REPL, approval gates
Added — biggest UX shift since v5.0
DevLoop now has a real interactive surface alongside the bash one-shots.
Companion TUI (cmd/devloop-tui/)
devloop(no args) → live Bubble Tea dashboard with session picker + pipeline detail, refreshed viafsnotifyon the event streamdevloop chat→ slash-command REPL:/plan,/run,/diff,/rollback,/status,/inbox,/mode ask|code|autodevloop status→ live single-session view- Single static Go binary. Opt-in:
make tui-installto~/.devloop/bin/. Bash falls back gracefully when the binary is absent.
Approval gates between pipeline stages
devloop runpauses after architect (plan review) and after worker (diff review)- Decide via TUI, gum,
/dev/tty, or pre-writeapprovals/<gate>.jsonfor CI — same contract - Skip everything with
--auto/-yorDEVLOOP_AUTO=1 - Reject-with-edit at the diff gate opens
$EDITORon the spec and re-runs the worker
devloop resume [TASK-ID]
- Picks up an interrupted pipeline from the last completed phase by replaying
events.ndjson --listshows resumable sessions,--dry-runprints the planned action
Structured event stream
.devloop/events.ndjsonis now the single source of truth for tooling- Per-session mirror at
.devloop/sessions/<TASK>/events.ndjson - Full schema in
docs/events.md - Silence with
DEVLOOP_EVENTS_DISABLED=1
Quality-of-life
devloop configure— gum-driven wizard (provider, models, permission mode, failover, auto-view).--non-interactive/--yesfor headless reruns. Config schema unchanged.devloop permissions— editor for.devloop/permissions.yaml(allow/deny patterns extending the built-in permission hook)- Always-visible status header during
devloop run/devloop resume:[arch ✓] [work ⠙] [review ·] [fix ·] TASK-… feature. Re-renders at each stage boundary. No-op when stdout isn't a TTY orDEVLOOP_STATUS_HEADER=off.
Architecture
The bash engine stays the engine. The TUI is a sibling Go binary that reads the event stream — it never parses bash output. Everything degrades: no Go binary → bash works; no gum → read prompt; no TTY → no-op headers. CI / scripted pipelines keep working unchanged.
Upgrade
devloop update # pulls v5.1.0
make tui-install # optional, builds the TUIFull changelog
See CHANGELOG.md and merged PR #1.
v5.0.4 — Permission hook: less noise, no double dialogs
Fixed — Claude permission prompts
Problem: Running devloop run as Claude architect triggered permission prompts for safe commands like devloop --help, devloop status, and for f in *.md; do head -5 "$f"; done.
Additionally, both the terminal [y/N] prompt AND the macOS dialog could fire simultaneously for the same request.
Fixed:
- Auto-approve all
devloopread commands (status, logs, help, view, check, sessions, recent, version) - Auto-approve for-loop file reads (common Claude pattern for reading specs)
- Auto-approve pipelines where every segment is a known-safe command
- macOS dialog only shows when terminal prompt was unavailable (no more double dialogs)
Upgrade: devloop update