Skip to content

t1160.1: Create build_cli_cmd() abstraction in supervisor/dispatch.sh#2053

Merged
marcusquinn merged 1 commit intomainfrom
feature/t1160.1
Feb 21, 2026
Merged

t1160.1: Create build_cli_cmd() abstraction in supervisor/dispatch.sh#2053
marcusquinn merged 1 commit intomainfrom
feature/t1160.1

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 21, 2026

Summary

Introduces build_cli_cmd() — a single semantic-to-CLI-specific command builder that replaces 6 duplicated if/else opencode-vs-claude branches (12+ total branches) across dispatch.sh.

What changed

  • New function: build_cli_cmd() accepts named flags (--cli, --action, --model, --title, --prompt, --output) and produces the correct CLI-specific command tokens
  • 3 actions: run (dispatch worker), version (health check), probe (model health)
  • 2 output modes: nul (NUL-delimited for process substitution) and array (printf %q for eval)
  • 6 call sites refactored: build_dispatch_cmd(), build_verify_dispatch_cmd(), do_prompt_repeat(), cmd_reprompt(), check_cli_health(), check_model_health()

Why

The opencode-vs-claude branching was duplicated 6 times with subtle differences (model prefix stripping for claude, title support for opencode only, different output format flags). This made it error-prone to add new CLI support or modify command structure. Now there's a single source of truth.

Verification

  • bash -n dispatch.sh — syntax valid
  • shellcheck -x -S warning dispatch.sh — zero violations
  • Manual testing of both output modes with multi-line prompts, quotes, and dollar signs
  • Verified claude CLI model prefix stripping (anthropic/claude-opus-4-6claude-opus-4-6)
  • Existing test suite passes (pre-existing failures unrelated to this change)

Pure refactor — no behavior change.

Ref #1747

Summary by CodeRabbit

  • Refactor

    • Consolidated internal command-building logic to reduce code duplication and improve consistency across dispatcher operations.
  • Documentation

    • Added detailed documentation for internal command construction patterns and usage conventions.

…ranches (t1160.1)

Introduces build_cli_cmd() — a single semantic-to-CLI-specific command builder
that replaces 6 duplicated if/else opencode-vs-claude branches across:
- build_dispatch_cmd() (NUL-delimited output)
- build_verify_dispatch_cmd() (NUL-delimited output)
- do_prompt_repeat() (array output)
- cmd_reprompt() (array output)
- check_cli_health() (version command)
- check_model_health() (probe command)

Supports 3 actions (run, version, probe) and 2 output modes (nul, array).
Pure refactor — no behavior change.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Walkthrough

A shell script refactor that introduces a centralized build_cli_cmd() function to consolidate fragmented CLI command construction logic across multiple call sites in the dispatch supervisor, replacing ad-hoc command assembly with a standardized builder abstraction.

Changes

Cohort / File(s) Summary
CLI Command Builder Refactoring
.agents/scripts/supervisor/dispatch.sh
Added new build_cli_cmd() function that centralizes opencode and claude dispatch, version, and probe command construction. Replaced scattered, duplicate command assembly across do_prompt_repeat, check_cli_health, and check_model_health with calls to the new builder, supporting both array and nul-delimited output modes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🔧 Commands once scattered, now aligned,

A builder true brings clarity refined,

From chaos born a single source of grace,

Dispatch commands in their rightful place! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: creating a build_cli_cmd() abstraction to consolidate CLI command building logic in dispatch.sh, which aligns with the substantial refactoring detailed in the PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/t1160.1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 34 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sat Feb 21 03:23:27 UTC 2026: Code review monitoring started
Sat Feb 21 03:23:28 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 34

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 34
  • VULNERABILITIES: 0

Generated on: Sat Feb 21 03:23:30 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

@marcusquinn marcusquinn marked this pull request as ready for review February 21, 2026 03:25
@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the maintainability and extensibility of the dispatch.sh script by abstracting the logic for building CLI-specific commands. By consolidating previously duplicated if/else branching into a single, well-defined function, it eliminates inconsistencies and simplifies the process of adding new CLI support or modifying command structures. This refactor ensures a single source of truth for command construction, leading to a more robust and easier-to-understand codebase without altering existing behavior.

Highlights

  • New Abstraction Function: Introduced a new build_cli_cmd() function to centralize the logic for constructing CLI-specific commands, replacing duplicated if/else branching.
  • Refactored Call Sites: Refactored six existing functions (build_dispatch_cmd, build_verify_dispatch_cmd, do_prompt_repeat, cmd_reprompt, check_cli_health, check_model_health) to utilize the new build_cli_cmd() abstraction.
  • Standardized Command Building: Standardized command building for run, version, and probe actions across different AI CLIs (opencode and claude), ensuring consistency.
  • Flexible Output Modes: Implemented two output modes (nul for NUL-delimited tokens and array for eval-compatible quoted tokens) for the new command builder to support various shell parsing needs.
Changelog
  • .agents/scripts/supervisor/dispatch.sh
    • Added build_cli_cmd() function to abstract CLI command construction.
    • Refactored do_prompt_repeat() to use build_cli_cmd() for dispatch command building.
    • Refactored check_cli_health() to use build_cli_cmd() for version command building.
    • Refactored check_model_health() to use build_cli_cmd() for probe command building.
    • Refactored build_dispatch_cmd() to use build_cli_cmd() for dispatch command building.
    • Refactored build_verify_dispatch_cmd() to use build_cli_cmd() for verify dispatch command building.
    • Refactored cmd_reprompt() to use build_cli_cmd() for reprompt command building.
Activity
  • No human activity (comments, reviews, etc.) has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a new function build_cli_cmd() to centralize the logic for constructing CLI commands for different AI clients (opencode and claude). This refactoring significantly reduces code duplication and improves maintainability, as noted in the PR description. The changes correctly integrate the new abstraction into existing call sites, including do_prompt_repeat(), check_cli_health(), check_model_health(), build_dispatch_cmd(), and build_verify_dispatch_cmd(). While printf '%q ' is used for quoting, the use of eval for array construction, though common, is noted as an area where direct bash array manipulation is generally preferred for robustness and safety, aligning with best practices for dynamic command construction. The NUL-delimited output for process substitution is also well-implemented. The PR adheres to the shell scripting quality standards by using local var="$1" patterns and explicit return statements. Overall, this is a well-executed refactoring that enhances the code's structure and reduces complexity.

Comment on lines +1029 to +1035
eval "cmd_parts=($(build_cli_cmd \
--cli "$ai_cli" \
--action run \
--output array \
--model "$tmodel" \
--title "$session_title" \
--prompt "$reinforced_prompt"))"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While printf '%q ' is used to quote arguments, eval for dynamic command construction, even with careful quoting, can still introduce risks. It's generally more robust and safer to use bash arrays directly for dynamic command construction to avoid eval entirely and simplify argument handling, aligning with best practices for shell scripting.

References
  1. Use bash arrays for dynamic command construction to avoid duplicating argument expansion patterns (e.g., safe expansion for empty arrays) across conditional branches like sudo vs non-sudo paths.

fi
# t1160.1: Build version command via build_cli_cmd abstraction
local -a version_cmd=()
eval "version_cmd=($(build_cli_cmd --cli "$ai_cli" --action version --output array))"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to other instances, while printf '%q ' helps quote arguments, eval for dynamic command construction can still introduce risks. It's generally more robust and safer to use bash arrays directly for dynamic command construction to avoid eval entirely and simplify argument handling, aligning with best practices for shell scripting.

References
  1. Use bash arrays for dynamic command construction to avoid duplicating argument expansion patterns (e.g., safe expansion for empty arrays) across conditional branches like sudo vs non-sudo paths.

fi
# t1160.1: Build probe command via build_cli_cmd abstraction
local -a probe_cmd=()
eval "probe_cmd=($(build_cli_cmd --cli "$ai_cli" --action probe --output array --model "$model"))"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

As noted elsewhere, while printf '%q ' helps quote arguments, eval for dynamic command construction can still introduce risks. It's generally more robust and safer to use bash arrays directly for dynamic command construction to avoid eval entirely and simplify argument handling, aligning with best practices for shell scripting.

References
  1. Use bash arrays for dynamic command construction to avoid duplicating argument expansion patterns (e.g., safe expansion for empty arrays) across conditional branches like sudo vs non-sudo paths.

Comment on lines +3299 to +3305
eval "cmd_parts=($(build_cli_cmd \
--cli "$ai_cli" \
--action run \
--output array \
--model "$resolved_model" \
--title "$retry_title" \
--prompt "$reprompt_msg"))"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While printf '%q ' is used to quote arguments, eval for dynamic command construction, even with careful quoting, can still introduce risks. It's generally more robust and safer to use bash arrays directly for dynamic command construction to avoid eval entirely and simplify argument handling, aligning with best practices for shell scripting.

References
  1. Use bash arrays for dynamic command construction to avoid duplicating argument expansion patterns (e.g., safe expansion for empty arrays) across conditional branches like sudo vs non-sudo paths.

coderabbitai[bot]
coderabbitai bot previously requested changes Feb 21, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
.agents/scripts/supervisor/dispatch.sh (2)

1028-1035: No guard against empty cmd_parts if build_cli_cmd fails.

The eval "cmd_parts=($(build_cli_cmd ...))" pattern is correct for deserializing printf %q output, and in practice build_cli_cmd won't fail here since --cli and --action are always provided. However, if build_cli_cmd ever returns non-zero (e.g., future action added, typo in a flag), cmd_parts silently becomes an empty array, leading to an empty exec line in the dispatch script.

This same pattern appears at Lines 1575, 1708, and 3299.

🛡️ Optional: guard against empty cmd_parts
 	eval "cmd_parts=($(build_cli_cmd \
 		--cli "$ai_cli" \
 		--action run \
 		--output array \
 		--model "$tmodel" \
 		--title "$session_title" \
 		--prompt "$reinforced_prompt"))"
+	if [[ ${`#cmd_parts`[@]} -eq 0 ]]; then
+		log_error "do_prompt_repeat: build_cli_cmd produced empty command"
+		return 1
+	fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/dispatch.sh around lines 1028 - 1035, The eval
assignment that deserializes build_cli_cmd into local -a cmd_parts=() can yield
an empty array when build_cli_cmd fails; update the code around the eval
"cmd_parts=($(build_cli_cmd ...))" to capture build_cli_cmd's exit status (or
check whether cmd_parts is empty) immediately after the eval, and if
non-zero/empty log an error and exit non‑zero (or skip the exec) to avoid
executing an empty command; apply the same guard to the other occurrences of
this pattern (the eval usages that populate cmd_parts) so you don't call exec
with an empty array.

1894-1896: Minor: _emit_token leaks into global scope after first build_cli_cmd call.

Bash nested functions become globally visible. After the first invocation of build_cli_cmd, _emit_token persists as a global function. It works correctly here because it's only called within build_cli_cmd where local -a _tokens=() is in scope, but if another function later defines or calls _emit_token, there could be unexpected behavior.

Not a practical issue today (no other callers exist), but worth noting for future-proofing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/dispatch.sh around lines 1894 - 1896, The nested
helper function _emit_token defined inside build_cli_cmd leaks into the global
function namespace; change build_cli_cmd to clean it up by unsetting the
function after use (call unset -f _emit_token at the end of build_cli_cmd) or
use a uniquely namespaced name (e.g., __build_cli_cmd_emit_token) for the helper
and then unset it; update the code around the local -a _tokens=() and
_emit_token() declaration so the helper is removed (unset -f _emit_token or
unset -f __build_cli_cmd_emit_token) before build_cli_cmd returns to avoid
global leakage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/scripts/supervisor/dispatch.sh:
- Around line 1726-1730: The fallback wait invocation currently uses "wait
\"$probe_pid\" 2>/dev/null || true", which masks the real exit code because the
"|| true" sets "$?" to 0; change the logic to run wait without immediately
chaining "|| true", capture its exit status into probe_exit (e.g., run wait
"$probe_pid" 2>/dev/null; probe_exit=$?; if wait itself failed and you still
need to ignore that specific failure then handle that case explicitly), and only
then apply any fallback/default value (like setting probe_exit=124 when
appropriate); update references to probe_exit and probe_pid so the probe result
check uses the captured exit code rather than the result of the "|| true" guard.

---

Nitpick comments:
In @.agents/scripts/supervisor/dispatch.sh:
- Around line 1028-1035: The eval assignment that deserializes build_cli_cmd
into local -a cmd_parts=() can yield an empty array when build_cli_cmd fails;
update the code around the eval "cmd_parts=($(build_cli_cmd ...))" to capture
build_cli_cmd's exit status (or check whether cmd_parts is empty) immediately
after the eval, and if non-zero/empty log an error and exit non‑zero (or skip
the exec) to avoid executing an empty command; apply the same guard to the other
occurrences of this pattern (the eval usages that populate cmd_parts) so you
don't call exec with an empty array.
- Around line 1894-1896: The nested helper function _emit_token defined inside
build_cli_cmd leaks into the global function namespace; change build_cli_cmd to
clean it up by unsetting the function after use (call unset -f _emit_token at
the end of build_cli_cmd) or use a uniquely namespaced name (e.g.,
__build_cli_cmd_emit_token) for the helper and then unset it; update the code
around the local -a _tokens=() and _emit_token() declaration so the helper is
removed (unset -f _emit_token or unset -f __build_cli_cmd_emit_token) before
build_cli_cmd returns to avoid global leakage.

Comment on lines +1726 to 1730
probe_exit=124
else
local probe_pid probe_tmpfile
probe_tmpfile=$(mktemp)
push_cleanup "rm -f '${probe_tmpfile}'"
("${probe_cmd[@]}" >"$probe_tmpfile" 2>&1) &
probe_pid=$!
local waited=0
while kill -0 "$probe_pid" 2>/dev/null && [[ "$waited" -lt 15 ]]; do
sleep 1
waited=$((waited + 1))
done
if kill -0 "$probe_pid" 2>/dev/null; then
kill "$probe_pid" 2>/dev/null || true
wait "$probe_pid" 2>/dev/null || true
probe_exit=124
else
wait "$probe_pid" 2>/dev/null || true
probe_exit=$?
fi
probe_result=$(cat "$probe_tmpfile" 2>/dev/null || true)
rm -f "$probe_tmpfile"
wait "$probe_pid" 2>/dev/null || true
probe_exit=$?
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: || true masks probe exit code in the no-timeout fallback path.

On Line 1728, wait "$probe_pid" 2>/dev/null || true causes $? to always be 0 (from true), so probe_exit on Line 1729 is always 0 regardless of the actual probe outcome. A failed probe that emits non-empty error output (e.g., a CLI error message) would bypass the [[ -z "$probe_result" && "$probe_exit" -ne 0 ]] check at Line 1754 and be cached as healthy.

🐛 Fix: capture exit code before the || guard
 		else
-			wait "$probe_pid" 2>/dev/null || true
-			probe_exit=$?
+			wait "$probe_pid" 2>/dev/null
+			probe_exit=$?
 		fi

If wait itself can fail for reasons other than the child's exit code (unlikely here since the PID is valid), an alternative is:

-			wait "$probe_pid" 2>/dev/null || true
-			probe_exit=$?
+			probe_exit=0
+			wait "$probe_pid" 2>/dev/null || probe_exit=$?
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
probe_exit=124
else
local probe_pid probe_tmpfile
probe_tmpfile=$(mktemp)
push_cleanup "rm -f '${probe_tmpfile}'"
("${probe_cmd[@]}" >"$probe_tmpfile" 2>&1) &
probe_pid=$!
local waited=0
while kill -0 "$probe_pid" 2>/dev/null && [[ "$waited" -lt 15 ]]; do
sleep 1
waited=$((waited + 1))
done
if kill -0 "$probe_pid" 2>/dev/null; then
kill "$probe_pid" 2>/dev/null || true
wait "$probe_pid" 2>/dev/null || true
probe_exit=124
else
wait "$probe_pid" 2>/dev/null || true
probe_exit=$?
fi
probe_result=$(cat "$probe_tmpfile" 2>/dev/null || true)
rm -f "$probe_tmpfile"
wait "$probe_pid" 2>/dev/null || true
probe_exit=$?
fi
probe_exit=124
else
wait "$probe_pid" 2>/dev/null
probe_exit=$?
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/dispatch.sh around lines 1726 - 1730, The
fallback wait invocation currently uses "wait \"$probe_pid\" 2>/dev/null ||
true", which masks the real exit code because the "|| true" sets "$?" to 0;
change the logic to run wait without immediately chaining "|| true", capture its
exit status into probe_exit (e.g., run wait "$probe_pid" 2>/dev/null;
probe_exit=$?; if wait itself failed and you still need to ignore that specific
failure then handle that case explicitly), and only then apply any
fallback/default value (like setting probe_exit=124 when appropriate); update
references to probe_exit and probe_pid so the probe result check uses the
captured exit code rather than the result of the "|| true" guard.

@marcusquinn marcusquinn dismissed coderabbitai[bot]’s stale review February 21, 2026 03:50

Auto-dismissed: bot review does not block autonomous pipeline

@marcusquinn marcusquinn merged commit 86bb1cb into main Feb 21, 2026
23 checks passed
@marcusquinn marcusquinn deleted the feature/t1160.1 branch February 21, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant