feat: headless dispatch docs + runner-helper.sh (t109.1, t109.2)#348
feat: headless dispatch docs + runner-helper.sh (t109.1, t109.2)#348marcusquinn merged 2 commits intomainfrom
Conversation
- Create tools/ai-assistants/headless-dispatch.md documenting OpenCode headless patterns: opencode run, opencode serve, --attach warm server, SDK parallel dispatch, custom agents, CI/CD integration - Create runner-helper.sh for named headless agent instances with create/run/status/list/edit/logs/stop/destroy commands - Rename 'droid' to 'runner' to avoid Factory.ai naming conflict - Update TODO.md, PLANS.md, subagent-index.toon, README.md
|
Caution Review failedThe pull request is closed. WalkthroughThis PR adds a new runner management system: a Bash CLI ( Changes
Sequence DiagramsequenceDiagram
participant User
participant RunnerCLI as runner-helper.sh
participant FS as Filesystem
participant OpenCode as OpenCode Server
participant SessionMgr as Session Manager
User->>RunnerCLI: run <runner> <task> [--attach|--model|--continue]
RunnerCLI->>FS: read `runners/<name>/config.json` and `AGENTS.md`
FS-->>RunnerCLI: return config, agent context
RunnerCLI->>RunnerCLI: compose prompt (AGENTS.md + user task)
RunnerCLI->>OpenCode: POST /run (prompt, model, session-id)
OpenCode->>SessionMgr: validate/resume session
SessionMgr-->>OpenCode: session context (restored or new)
OpenCode-->>RunnerCLI: stream execution output / exit code
RunnerCLI->>FS: write run log to `runs/`, update `config.json` (lastRun, lastStatus, runCount)
RunnerCLI-->>User: display output, status, and run metadata
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello @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 enhances the framework for headless AI agent operations by introducing a dedicated command-line tool for managing persistent agent instances and providing extensive documentation on various dispatch methods. These changes aim to streamline the integration of AI agents into automated workflows, enabling more efficient parallel processing and CI/CD integration, while also clarifying naming conventions for better product alignment. Highlights
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Thu Feb 5 21:17:48 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
There was a problem hiding this comment.
Code Review
This pull request introduces a significant and well-implemented feature for managing headless AI agents, runner-helper.sh, accompanied by excellent, comprehensive documentation in headless-dispatch.md. The code is generally of high quality, adhering to good shell scripting practices. The renaming from "droid" to "runner" is applied consistently across all files. I've identified one high-severity issue in the JSON generation logic that could produce invalid output and one medium-severity efficiency improvement. The rest of the changes are solid.
| if [[ "$output_format" == "json" ]]; then | ||
| local json_output="[" | ||
| local first=true | ||
| for runner_path in $runners; do | ||
| local rname | ||
| rname=$(basename "$runner_path") | ||
| local config_file="$runner_path/config.json" | ||
| if [[ -f "$config_file" ]]; then | ||
| if [[ "$first" == "true" ]]; then | ||
| first=false | ||
| else | ||
| json_output+="," | ||
| fi | ||
| json_output+=$(cat "$config_file") | ||
| fi | ||
| done | ||
| json_output+="]" | ||
| echo "$json_output" | jq '.' | ||
| return 0 | ||
| fi |
There was a problem hiding this comment.
The current implementation for building the JSON output is not robust. It manually concatenates strings and file contents (json_output+=$(cat "$config_file")), which will fail if the config.json files contain characters that need escaping or if the concatenation results in invalid JSON (e.g., due to extra or missing commas). This can lead to malformed JSON output, breaking any downstream tools that consume it. A much safer and more efficient approach is to use jq to construct the entire JSON array.
| if [[ "$output_format" == "json" ]]; then | |
| local json_output="[" | |
| local first=true | |
| for runner_path in $runners; do | |
| local rname | |
| rname=$(basename "$runner_path") | |
| local config_file="$runner_path/config.json" | |
| if [[ -f "$config_file" ]]; then | |
| if [[ "$first" == "true" ]]; then | |
| first=false | |
| else | |
| json_output+="," | |
| fi | |
| json_output+=$(cat "$config_file") | |
| fi | |
| done | |
| json_output+="]" | |
| echo "$json_output" | jq '.' | |
| return 0 | |
| fi | |
| if [[ "$output_format" == "json" ]]; then | |
| # Use jq to safely construct the JSON array. This is more robust | |
| # than string concatenation. | |
| local config_files=() | |
| while IFS= read -r runner_path; do | |
| local config_file="$runner_path/config.json" | |
| if [[ -f "$config_file" ]]; then | |
| config_files+=("$config_file") | |
| fi | |
| done <<< "$runners" | |
| if (( ${#config_files[@]} > 0 )); then | |
| jq -s . "${config_files[@]}" | |
| else | |
| echo "[]" | |
| fi | |
| return 0 | |
| fi |
.agent/scripts/runner-helper.sh
Outdated
| description=$(jq -r '.description // "N/A"' "$config") | ||
| model=$(jq -r '.model // "N/A"' "$config") | ||
| workdir=$(jq -r '.workdir // "N/A"' "$config") | ||
| created=$(jq -r '.created // "N/A"' "$config") | ||
| last_run=$(jq -r '.lastRun // "never"' "$config") | ||
| last_status=$(jq -r '.lastStatus // "N/A"' "$config") | ||
| run_count=$(jq -r '.runCount // 0' "$config") | ||
| last_duration=$(jq -r '.lastDuration // "N/A"' "$config") |
There was a problem hiding this comment.
This function calls jq eight separate times to read different keys from the same config.json file. This is inefficient as it involves multiple process invocations and file reads for a single operation. You can optimize this by reading all required values in a single jq call, for example by outputting them as a JSON object or as newline-separated values and then parsing them in bash. This will improve performance and reduce resource usage.
…te jq calls in status
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Thu Feb 5 21:21:51 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
|
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
TODO.md (2)
294-298:⚠️ Potential issue | 🔴 CriticalCRITICAL: Unresolved merge conflict in TOON backlog header.
The merge conflict markers indicate conflicting backlog counts (60 vs 80 items), which suggests significant divergence between branches. This prevents TOON parsers and task management tools from functioning correctly.
Action Required:
- Review both versions of the backlog to identify which tasks are unique to each branch
- Manually merge the task lists, preserving all valid entries
- Update the count in the TOON header to reflect the final merged total
- Remove all conflict markers (<<<<<<< HEAD, =======, >>>>>>> origin/main)
📋 Resolution guidance
Compare the task entries between lines 299-328 in both versions:
- HEAD version (lines 299-307): Contains older t109, t110, t111 as separate tasks
- origin/main version (lines 309-328): Contains expanded t109 with subtasks and additional t110-t118
The origin/main version appears more current given that t109.1 and t109.2 are marked completed (lines 215-218). You'll need to reconcile which tasks from HEAD should be preserved if they're not already in origin/main.
311-311:⚠️ Potential issue | 🟡 MinorNaming inconsistency: "droid-helper.sh" should be "runner-helper.sh".
Line 311 references "droid-helper.sh" in the TOON block for t109.2, but the notes at line 218 explicitly state the script was "renamed from 'droid' to 'runner' to avoid Factory.ai naming conflict."
After resolving the merge conflicts, ensure this entry uses "runner-helper.sh" to maintain consistency with the completed rename.
🔧 Suggested fix (post-merge)
-t109.2,Create droid-helper.sh,,agents|scripts|dispatch,4h,4h,,2026-02-03T00:00Z,pending,,,t109 +t109.2,Create runner-helper.sh,,agents|scripts|dispatch,4h,4h,,2026-02-03T00:00Z,completed,,,t109Note: Also update status from
pendingtocompletedsince line 217 indicates t109.2 was completed on 2026-02-05.
🤖 Fix all issues with AI agents
In @.agent/scripts/runner-helper.sh:
- Around line 380-389: The timeout invocation is non-portable and cmd_timeout is
not validated; update the runner to detect an available timeout binary (try
gtimeout then timeout) and use that binary via a local variable (e.g.,
timeout_cmd) instead of calling timeout directly, and validate cmd_timeout
before use (ensure it's non-empty, an integer, and >0) falling back to running
the command without a timeout if invalid; locate and change the block using
cmd_timeout, cmd_args, and log_file in runner-helper.sh to perform the
detection/validation and then invoke "$timeout_cmd" "$cmd_timeout"
"${cmd_args[@]}" 2>&1 | tee "$log_file" only when a valid timeout is present.
- Around line 332-343: The script currently reads $dir/session.id for
continuation but never writes it; update the runner-helper.sh flow that calls
"opencode run" so after the run completes you parse the command output (prefer
JSON via --format json if supported or otherwise extract the session id from the
stdout), and write the found session ID to "$dir/session.id"; on non-zero exit
or if no session id is found, remove/clear "$dir/session.id" to avoid stale IDs;
ensure this logic is applied where cmd_args and continue_session are used and
that log_warn/log_info reflect success or clearing.
In @.agent/tools/ai-assistants/headless-dispatch.md:
- Around line 63-495: This file contains lengthy inline CLI/SDK/HTTP examples
under headings like "Dispatch Methods" and "Parallel Execution"; replace those
embedded blocks with progressive-disclosure pointers to the canonical
implementations (e.g., reference the dispatch flow in runner-helper.sh and API
examples in tools/ai-assistants/opencode-server.md using file:line style) and
move the full example content into the owning docs/scripts; apply this pattern
across `.agent/**/*.md` (remove large code fences under "Method 1/2/3/4",
"Session Management", "Parallel Execution", "Runners", and "CI/CD Integration"
and replace them with brief one-line pointers to the authoritative files you
moved the examples to).
- Around line 21-26: The quick reference list omits the `edit` and `logs`
commands for runner-helper; update the documentation block listing the command
summaries (the lines referencing `opencode run`, `opencode serve`, `npm install
`@opencode-ai/sdk``, and especially `runner-helper.sh
[create|run|status|list|stop|destroy]`) to include the full runner-helper
command set by adding `edit` and `logs` into the bracketed options (i.e.,
`runner-helper.sh [create|run|status|list|stop|destroy|edit|logs]`) and ensure
any brief descriptions or examples elsewhere in this section mention how to use
`runner-helper.sh edit` and `runner-helper.sh logs`.
- Around line 259-269: Update the runner directory example so it matches the
runner-helper script output by adding the missing session.id file and runs/
directory to the displayed tree (i.e., include "session.id" and a "runs/" entry
under each runner like code-reviewer and seo-analyst); edit the tree in the
.agent/tools/ai-assistants/headless-dispatch.md example block (the snippet
showing ~/.aidevops/.agent-workspace/runners/) so operators see the exact layout
produced by the script.
In `@todo/PLANS.md`:
- Around line 1974-1980: The TOON plan block uses duplicate IDs (p016, m064,
d039) which collide with existing entries; update the plan block's identifiers
to the next free TOON IDs (e.g., replace p016 with a new p### and update its
related milestone/decision IDs m###/d###) and ensure the same renumbering is
applied to the other affected blocks (lines referenced in the comment: 2080-2085
and 2106-2109) so all TOON tags are unique; also rename any milestone/decision
text that still references "droid-helper.sh" to the new runner name to keep
naming consistent (locate the TOON marker comment starting with "<!--TOON:plan"
and update IDs and text inside that block plus its matching m/d entries).
| # Continue previous session if requested | ||
| if [[ "$continue_session" == "true" ]]; then | ||
| local session_id="" | ||
| if [[ -f "$dir/session.id" ]]; then | ||
| session_id=$(cat "$dir/session.id") | ||
| fi | ||
| if [[ -n "$session_id" ]]; then | ||
| cmd_args+=("-s" "$session_id") | ||
| else | ||
| log_warn "No previous session found for $name, starting fresh" | ||
| fi | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the runner-helper.sh file to understand the full context
wc -l .agent/scripts/runner-helper.shRepository: marcusquinn/aidevops
Length of output: 99
🏁 Script executed:
# Read the specific sections mentioned in the review (332-343 and 395-408)
sed -n '320,410p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 2911
🏁 Script executed:
# Search for opencode run invocations and how session IDs are handled
rg "opencode run" .agent/scripts/runner-helper.sh -A 5 -B 5Repository: marcusquinn/aidevops
Length of output: 360
🏁 Script executed:
# Check if there's any session ID extraction or persistence logic
rg "session\.id" .agent/scripts/runner-helper.sh -A 3 -B 3Repository: marcusquinn/aidevops
Length of output: 1183
🏁 Script executed:
# Search for any JSON parsing or output format handling in the script
rg "jq|json|format" .agent/scripts/runner-helper.sh | head -20Repository: marcusquinn/aidevops
Length of output: 1051
🏁 Script executed:
# Search for any extraction of session IDs from command output elsewhere in the repo
rg "session" .agent/scripts/ -lRepository: marcusquinn/aidevops
Length of output: 1556
🏁 Script executed:
# Look at the full context of where opencode run is executed to see output handling
sed -n '350,420p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 2261
🏁 Script executed:
# Search for opencode CLI documentation or examples
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "*.sh" \) -exec rg -l "opencode.*--help|opencode.*format|opencode.*session" {} \;Repository: marcusquinn/aidevops
Length of output: 530
🏁 Script executed:
# Check if there's any README or documentation about opencode
rg "opencode" README.md -A 5 -B 5 2>/dev/null | head -40Repository: marcusquinn/aidevops
Length of output: 1887
🏁 Script executed:
# Look for opencode test helper to see how it uses opencode run
cat .agent/scripts/opencode-test-helper.sh | head -100Repository: marcusquinn/aidevops
Length of output: 3520
🏁 Script executed:
# Search for any session ID extraction patterns in all scripts
rg "session_id.*\$\(" .agent/scripts/ -A 2 -B 2Repository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Check opencode documentation in the tools directory
ls -la .agent/tools/opencode/Repository: marcusquinn/aidevops
Length of output: 389
🏁 Script executed:
# Look at opencode-server documentation for session details
cat .agent/tools/opencode/opencode-server.md | grep -i "session\|output\|format" -A 3 -B 1Repository: marcusquinn/aidevops
Length of output: 136
🏁 Script executed:
# Search for any patterns of capturing command output to extract info
rg "opencode.*\|.*jq|output.*session" .agent/scripts/ -A 3Repository: marcusquinn/aidevops
Length of output: 357
🏁 Script executed:
# Check the main opencode documentation
cat .agent/tools/opencode/opencode.md | grep -i "session\|output.*format\|--format\|--print\|json" -A 3 -B 1 | head -80Repository: marcusquinn/aidevops
Length of output: 2202
🏁 Script executed:
# Look for any references to how session IDs are captured or used in workflows
rg "session.*json\|print.*session\|format.*json" .agent/workflows/ -A 2Repository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Check how opencode run is used in other helpers
rg "opencode run" .agent/scripts/*.sh -B 2 -A 8 | head -100Repository: marcusquinn/aidevops
Length of output: 6416
🏁 Script executed:
# Look at the full opencode.md documentation for session/output information
wc -l .agent/tools/opencode/opencode.mdRepository: marcusquinn/aidevops
Length of output: 101
🏁 Script executed:
# Search for specific mention of JSON output or session in opencode.md
cat .agent/tools/opencode/opencode.md | grep -i "json\|session\|--format\|output" -A 2 -B 1 | head -150Repository: marcusquinn/aidevops
Length of output: 3368
🏁 Script executed:
# Look at the complete runner-helper function to understand full context (lines 280-420)
sed -n '280,420p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 4900
🏁 Script executed:
# Check if there's any mechanism to extract session ID from opencode logs or output
rg "session.*extract|extract.*session|sed.*session|jq.*session" .agent/scripts/ -A 2Repository: marcusquinn/aidevops
Length of output: 5898
🏁 Script executed:
# Look at the runner_stop function to see how it uses session.id (should be around line 395-408)
sed -n '395,420p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 984
🏁 Script executed:
# Let's search for how opencode outputs session information or if there are any examples of parsing its output
rg "\"session\"|'session'|session.*:.*\|" .agent/scripts/ -A 1 | head -50Repository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Check if runner_stop function uses the session.id file (the second location mentioned: 395-408)
sed -n '395,450p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 1931
🏁 Script executed:
# Look for any documentation or examples of opencode output format
cat .agent/tools/opencode/opencode.md | tail -200Repository: marcusquinn/aidevops
Length of output: 6096
🏁 Script executed:
# Search for how opencode server or API might expose session IDs
rg "opencode.*serve\|--session\|-s\|session" .agent/tools/opencode/opencode.md -A 2 -B 1Repository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Search for any documentation about opencode output or session management
rg "session.*ID|ID.*session|opencode.*output|emit.*session" .agent/tools/ -A 2Repository: marcusquinn/aidevops
Length of output: 3577
🏁 Script executed:
# Check if there's a runner_stop function or any function that deals with stopping sessions
rg "cmd_stop|runner_stop|stop.*session" .agent/scripts/runner-helper.sh -A 15Repository: marcusquinn/aidevops
Length of output: 1929
🏁 Script executed:
# Look for any indication of how session IDs should be captured
rg "\-s.*session|session.*flag|continue.*session" .agent/scripts/runner-helper.sh -B 2 -A 2Repository: marcusquinn/aidevops
Length of output: 1120
🏁 Script executed:
# Check the full runner_stop context around lines mentioned (also check 395-408 area)
sed -n '450,550p' .agent/scripts/runner-helper.sh | cat -nRepository: marcusquinn/aidevops
Length of output: 3662
🏁 Script executed:
# Find the full cmd_stop implementation
rg "^cmd_stop" .agent/scripts/runner-helper.sh -A 30Repository: marcusquinn/aidevops
Length of output: 742
🏁 Script executed:
# Search for where session.id might be written or cleared
rg "session\.id.*>" .agent/scripts/runner-helper.shRepository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Look for any patterns of capturing output from opencode run
sed -n '300,450p' .agent/scripts/runner-helper.sh | grep -A 10 "cmd_stop"Repository: marcusquinn/aidevops
Length of output: 46
🏁 Script executed:
# Check if there's any session ID extraction or parsing logic anywhere
rg "sed.*session|grep.*session|session.*extract" .agent/scripts/ -A 2Repository: marcusquinn/aidevops
Length of output: 3899
🏁 Script executed:
# Look at the headless-dispatch documentation more carefully for session capture patterns
cat .agent/tools/ai-assistants/headless-dispatch.md | grep -A 10 "SESSION_ID"Repository: marcusquinn/aidevops
Length of output: 1119
Capture and persist session IDs so session continuation and abort work reliably.
The helper reads session.id for --continue and stop operations, but never writes it after a run executes. This breaks both session resumption and the runner-helper stop function, which expects $dir/session.id to exist.
After opencode run completes, extract the session ID from its output and persist it to $dir/session.id. Clear the file on failed runs to avoid stale IDs. This requires understanding how opencode run exposes the session ID—check if it supports --format json, has a --print-session flag, or outputs session info in the default format.
🤖 Prompt for AI Agents
In @.agent/scripts/runner-helper.sh around lines 332 - 343, The script currently
reads $dir/session.id for continuation but never writes it; update the
runner-helper.sh flow that calls "opencode run" so after the run completes you
parse the command output (prefer JSON via --format json if supported or
otherwise extract the session id from the stdout), and write the found session
ID to "$dir/session.id"; on non-zero exit or if no session id is found,
remove/clear "$dir/session.id" to avoid stale IDs; ensure this logic is applied
where cmd_args and continue_session are used and that log_warn/log_info reflect
success or clearing.
| # Execute with timeout | ||
| local exit_code=0 | ||
| local start_time | ||
| start_time=$(date +%s) | ||
|
|
||
| if timeout "$cmd_timeout" "${cmd_args[@]}" 2>&1 | tee "$log_file"; then | ||
| exit_code=0 | ||
| else | ||
| exit_code=$? | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's verify the file exists and examine the code around lines 380-389
wc -l .agent/scripts/runner-helper.shRepository: marcusquinn/aidevops
Length of output: 99
🏁 Script executed:
# Read the relevant section of the file to see the actual code
sed -n '370,400p' .agent/scripts/runner-helper.shRepository: marcusquinn/aidevops
Length of output: 828
🏁 Script executed:
# Search for existing timeout-related code throughout the scripts directory
rg -n "timeout|gtimeout" .agent/scripts/ -A 2 -B 2Repository: marcusquinn/aidevops
Length of output: 35337
🏁 Script executed:
# Check for any documentation about setup, dependencies, or timeout handling
rg -n "timeout|coreutils|gtimeout|macOS|Darwin" README.md setup.sh .agent/ --type-listRepository: marcusquinn/aidevops
Length of output: 453
🏁 Script executed:
# Look for where cmd_timeout is defined and validated
rg -n "cmd_timeout" .agent/scripts/runner-helper.sh -B 5 -A 5Repository: marcusquinn/aidevops
Length of output: 1306
Add timeout portability check and validate --timeout value to ensure reliability across platforms.
The timeout command isn't available on macOS by default (requires coreutils/gtimeout). Also, cmd_timeout is never validated—empty, non-numeric, or negative values will cause failures. Other scripts in .agent/scripts/ (e.g., linters-local.sh, gsc-sitemap-helper.sh) already implement both patterns; apply the same here for consistency.
🛠️ Proposed fix
# Execute with timeout
+ local timeout_cmd="timeout"
+ if ! command -v "$timeout_cmd" &>/dev/null; then
+ if command -v gtimeout &>/dev/null; then
+ timeout_cmd="gtimeout"
+ else
+ log_error "timeout command required (install coreutils or gnu-coreutils)"
+ return 1
+ fi
+ fi
+ if ! [[ "$cmd_timeout" =~ ^[0-9]+$ ]] || [[ "$cmd_timeout" -le 0 ]]; then
+ log_error "--timeout must be a positive integer (seconds)"
+ return 1
+ fi
local exit_code=0
local start_time
start_time=$(date +%s)
- if timeout "$cmd_timeout" "${cmd_args[@]}" 2>&1 | tee "$log_file"; then
+ if "$timeout_cmd" "$cmd_timeout" "${cmd_args[@]}" 2>&1 | tee "$log_file"; then🤖 Prompt for AI Agents
In @.agent/scripts/runner-helper.sh around lines 380 - 389, The timeout
invocation is non-portable and cmd_timeout is not validated; update the runner
to detect an available timeout binary (try gtimeout then timeout) and use that
binary via a local variable (e.g., timeout_cmd) instead of calling timeout
directly, and validate cmd_timeout before use (ensure it's non-empty, an
integer, and >0) falling back to running the command without a timeout if
invalid; locate and change the block using cmd_timeout, cmd_args, and log_file
in runner-helper.sh to perform the detection/validation and then invoke
"$timeout_cmd" "$cmd_timeout" "${cmd_args[@]}" 2>&1 | tee "$log_file" only when
a valid timeout is present.
| - **One-shot dispatch**: `opencode run "prompt"` | ||
| - **Warm server dispatch**: `opencode run --attach http://localhost:4096 "prompt"` | ||
| - **Server mode**: `opencode serve [--port 4096]` | ||
| - **SDK**: `npm install @opencode-ai/sdk` | ||
| - **Runner management**: `runner-helper.sh [create|run|status|list|stop|destroy]` | ||
| - **Runner directory**: `~/.aidevops/.agent-workspace/runners/` |
There was a problem hiding this comment.
Quick reference omits edit and logs.
The runner-helper summary should include the full command set.
✏️ Suggested tweak
-- **Runner management**: `runner-helper.sh [create|run|status|list|stop|destroy]`
+- **Runner management**: `runner-helper.sh [create|run|status|list|edit|logs|stop|destroy]`📝 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.
| - **One-shot dispatch**: `opencode run "prompt"` | |
| - **Warm server dispatch**: `opencode run --attach http://localhost:4096 "prompt"` | |
| - **Server mode**: `opencode serve [--port 4096]` | |
| - **SDK**: `npm install @opencode-ai/sdk` | |
| - **Runner management**: `runner-helper.sh [create|run|status|list|stop|destroy]` | |
| - **Runner directory**: `~/.aidevops/.agent-workspace/runners/` | |
| - **One-shot dispatch**: `opencode run "prompt"` | |
| - **Warm server dispatch**: `opencode run --attach http://localhost:4096 "prompt"` | |
| - **Server mode**: `opencode serve [--port 4096]` | |
| - **SDK**: `npm install `@opencode-ai/sdk`` | |
| - **Runner management**: `runner-helper.sh [create|run|status|list|edit|logs|stop|destroy]` | |
| - **Runner directory**: `~/.aidevops/.agent-workspace/runners/` |
🤖 Prompt for AI Agents
In @.agent/tools/ai-assistants/headless-dispatch.md around lines 21 - 26, The
quick reference list omits the `edit` and `logs` commands for runner-helper;
update the documentation block listing the command summaries (the lines
referencing `opencode run`, `opencode serve`, `npm install `@opencode-ai/sdk``,
and especially `runner-helper.sh [create|run|status|list|stop|destroy]`) to
include the full runner-helper command set by adding `edit` and `logs` into the
bracketed options (i.e., `runner-helper.sh
[create|run|status|list|stop|destroy|edit|logs]`) and ensure any brief
descriptions or examples elsewhere in this section mention how to use
`runner-helper.sh edit` and `runner-helper.sh logs`.
| ## Dispatch Methods | ||
|
|
||
| ### Method 1: Direct CLI (`opencode run`) | ||
|
|
||
| Simplest approach. Each invocation starts a fresh session (or resumes one). | ||
|
|
||
| ```bash | ||
| # One-shot task | ||
| opencode run "Review src/auth.ts for security issues" | ||
|
|
||
| # With specific model | ||
| opencode run -m anthropic/claude-sonnet-4-20250514 "Generate unit tests for src/utils/" | ||
|
|
||
| # With specific agent | ||
| opencode run --agent plan "Analyze the database schema" | ||
|
|
||
| # JSON output (for parsing) | ||
| opencode run --format json "List all exported functions in src/" | ||
|
|
||
| # Attach files for context | ||
| opencode run -f ./schema.sql -f ./migration.ts "Generate types from this schema" | ||
|
|
||
| # Set a session title | ||
| opencode run --title "Auth review" "Review the auth middleware" | ||
| ``` | ||
|
|
||
| ### Method 2: Warm Server (`opencode serve` + `--attach`) | ||
|
|
||
| Avoids MCP server cold boot on every dispatch. Recommended for repeated tasks. | ||
|
|
||
| ```bash | ||
| # Terminal 1: Start persistent server | ||
| opencode serve --port 4096 | ||
|
|
||
| # Terminal 2+: Dispatch tasks against it | ||
| opencode run --attach http://localhost:4096 "Task 1" | ||
| opencode run --attach http://localhost:4096 --agent plan "Review task" | ||
| ``` | ||
|
|
||
| ### Method 3: SDK (TypeScript) | ||
|
|
||
| Full programmatic control. Best for parallel orchestration. | ||
|
|
||
| ```typescript | ||
| import { createOpencode, createOpencodeClient } from "@opencode-ai/sdk" | ||
|
|
||
| // Start server + client together | ||
| const { client, server } = await createOpencode({ | ||
| port: 4096, | ||
| config: { model: "anthropic/claude-sonnet-4-20250514" }, | ||
| }) | ||
|
|
||
| // Or connect to existing server | ||
| const client = createOpencodeClient({ | ||
| baseUrl: "http://localhost:4096", | ||
| }) | ||
| ``` | ||
|
|
||
| ### Method 4: HTTP API (curl) | ||
|
|
||
| Direct API calls for shell scripts and non-JS integrations. | ||
|
|
||
| ```bash | ||
| SERVER="http://localhost:4096" | ||
|
|
||
| # Create session | ||
| SESSION_ID=$(curl -sf -X POST "$SERVER/session" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"title": "API task"}' | jq -r '.id') | ||
|
|
||
| # Send prompt (sync - waits for response) | ||
| curl -sf -X POST "$SERVER/session/$SESSION_ID/message" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "model": {"providerID": "anthropic", "modelID": "claude-sonnet-4-20250514"}, | ||
| "parts": [{"type": "text", "text": "Explain this codebase"}] | ||
| }' | ||
|
|
||
| # Send prompt (async - returns 204 immediately) | ||
| curl -sf -X POST "$SERVER/session/$SESSION_ID/prompt_async" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"parts": [{"type": "text", "text": "Run tests in background"}]}' | ||
|
|
||
| # Monitor via SSE | ||
| curl -N "$SERVER/event" | ||
| ``` | ||
|
|
||
| ## Session Management | ||
|
|
||
| ### Resuming Sessions | ||
|
|
||
| ```bash | ||
| # Continue last session | ||
| opencode run -c "Continue where we left off" | ||
|
|
||
| # Resume specific session by ID | ||
| opencode run -s ses_abc123 "Add error handling to the auth module" | ||
| ``` | ||
|
|
||
| ### Forking Sessions | ||
|
|
||
| Create a branch from an existing conversation: | ||
|
|
||
| ```bash | ||
| # Via HTTP API | ||
| curl -sf -X POST "http://localhost:4096/session/$SESSION_ID/fork" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"messageID": "msg-123"}' | ||
| ``` | ||
|
|
||
| ```typescript | ||
| // Via SDK - create child session | ||
| const child = await client.session.create({ | ||
| body: { parentID: parentSession.id, title: "Subtask" }, | ||
| }) | ||
| ``` | ||
|
|
||
| ### Context Injection (No Reply) | ||
|
|
||
| Inject context without triggering an AI response: | ||
|
|
||
| ```typescript | ||
| await client.session.prompt({ | ||
| path: { id: sessionId }, | ||
| body: { | ||
| noReply: true, | ||
| parts: [{ | ||
| type: "text", | ||
| text: "Context: This project uses Express.js with TypeScript.", | ||
| }], | ||
| }, | ||
| }) | ||
| ``` | ||
|
|
||
| ## Parallel Execution | ||
|
|
||
| ### CLI Parallel (Background Jobs) | ||
|
|
||
| ```bash | ||
| # Start server once | ||
| opencode serve --port 4096 & | ||
|
|
||
| # Dispatch parallel tasks | ||
| opencode run --attach http://localhost:4096 --title "Review" \ | ||
| "Review src/auth/ for security issues" & | ||
| opencode run --attach http://localhost:4096 --title "Tests" \ | ||
| "Generate unit tests for src/utils/" & | ||
| opencode run --attach http://localhost:4096 --title "Docs" \ | ||
| "Generate API documentation for src/api/" & | ||
|
|
||
| wait # Wait for all to complete | ||
| ``` | ||
|
|
||
| ### SDK Parallel (Promise.all) | ||
|
|
||
| ```typescript | ||
| const client = createOpencodeClient({ baseUrl: "http://localhost:4096" }) | ||
|
|
||
| // Create parallel sessions | ||
| const [review, tests, docs] = await Promise.all([ | ||
| client.session.create({ body: { title: "Code Review" } }), | ||
| client.session.create({ body: { title: "Test Generation" } }), | ||
| client.session.create({ body: { title: "Documentation" } }), | ||
| ]) | ||
|
|
||
| // Dispatch tasks concurrently | ||
| await Promise.all([ | ||
| client.session.promptAsync({ | ||
| path: { id: review.data.id }, | ||
| body: { parts: [{ type: "text", text: "Review src/auth.ts" }] }, | ||
| }), | ||
| client.session.promptAsync({ | ||
| path: { id: tests.data.id }, | ||
| body: { parts: [{ type: "text", text: "Generate tests for src/utils/" }] }, | ||
| }), | ||
| client.session.promptAsync({ | ||
| path: { id: docs.data.id }, | ||
| body: { parts: [{ type: "text", text: "Generate API docs for src/api/" }] }, | ||
| }), | ||
| ]) | ||
|
|
||
| // Monitor via SSE | ||
| const events = await client.event.subscribe() | ||
| for await (const event of events.stream) { | ||
| if (event.type === "session.status") { | ||
| console.log(`Session ${event.properties.id}: ${event.properties.status}`) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Runners | ||
|
|
||
| Runners are named, persistent agent instances with their own identity, instructions, and optionally isolated memory. Managed by `runner-helper.sh`. | ||
|
|
||
| ### Directory Structure | ||
|
|
||
| ```text | ||
| ~/.aidevops/.agent-workspace/runners/ | ||
| ├── code-reviewer/ | ||
| │ ├── AGENTS.md # Runner personality/instructions | ||
| │ ├── config.json # Runner configuration | ||
| │ └── memory.db # Runner-specific memories (optional) | ||
| └── seo-analyst/ | ||
| ├── AGENTS.md | ||
| ├── config.json | ||
| └── memory.db | ||
| ``` | ||
|
|
||
| ### Runner Lifecycle | ||
|
|
||
| ```bash | ||
| # Create a runner | ||
| runner-helper.sh create code-reviewer \ | ||
| --description "Reviews code for security and quality" \ | ||
| --model anthropic/claude-sonnet-4-20250514 | ||
|
|
||
| # Run a task | ||
| runner-helper.sh run code-reviewer "Review src/auth/ for vulnerabilities" | ||
|
|
||
| # Run against warm server (faster) | ||
| runner-helper.sh run code-reviewer "Review src/auth/" --attach http://localhost:4096 | ||
|
|
||
| # Check status | ||
| runner-helper.sh status code-reviewer | ||
|
|
||
| # List all runners | ||
| runner-helper.sh list | ||
|
|
||
| # Destroy a runner | ||
| runner-helper.sh destroy code-reviewer | ||
| ``` | ||
|
|
||
| ### Custom Runner Instructions | ||
|
|
||
| Each runner gets its own `AGENTS.md` that defines its personality: | ||
|
|
||
| ```markdown | ||
| # Code Reviewer | ||
|
|
||
| You are a senior code reviewer focused on security and maintainability. | ||
|
|
||
| ## Rules | ||
|
|
||
| - Flag any use of eval(), innerHTML, or raw SQL | ||
| - Check for proper input validation | ||
| - Verify error handling covers edge cases | ||
| - Note missing tests for critical paths | ||
|
|
||
| ## Output Format | ||
|
|
||
| For each file reviewed, output: | ||
| 1. Severity (critical/warning/info) | ||
| 2. Line reference (file:line) | ||
| 3. Issue description | ||
| 4. Suggested fix | ||
| ``` | ||
|
|
||
| ### Integration with Memory | ||
|
|
||
| Runners can use isolated or shared memory: | ||
|
|
||
| ```bash | ||
| # Store a memory for a specific runner | ||
| memory-helper.sh store \ | ||
| --content "WORKING_SOLUTION: Use parameterized queries for SQL" \ | ||
| --tags "security,sql" \ | ||
| --namespace "code-reviewer" | ||
|
|
||
| # Recall from runner namespace | ||
| memory-helper.sh recall \ | ||
| --query "SQL injection" \ | ||
| --namespace "code-reviewer" | ||
| ``` | ||
|
|
||
| ### Integration with Mailbox | ||
|
|
||
| Runners communicate via the existing mailbox system: | ||
|
|
||
| ```bash | ||
| # Coordinator dispatches to runner | ||
| mail-helper.sh send \ | ||
| --to "code-reviewer" \ | ||
| --type "task_dispatch" \ | ||
| --payload "Review PR #123 for security issues" | ||
|
|
||
| # Runner reports back | ||
| mail-helper.sh send \ | ||
| --to "coordinator" \ | ||
| --type "status_report" \ | ||
| --from "code-reviewer" \ | ||
| --payload "Review complete. 2 critical, 5 warnings found." | ||
| ``` | ||
|
|
||
| ## Custom Agents for Dispatch | ||
|
|
||
| OpenCode supports custom agents via markdown files or JSON config. These complement runners by defining tool access and permissions. | ||
|
|
||
| ### Markdown Agent (Project-Level) | ||
|
|
||
| Place in `.opencode/agents/security-reviewer.md`: | ||
|
|
||
| ```markdown | ||
| --- | ||
| description: Security-focused code reviewer | ||
| mode: subagent | ||
| model: anthropic/claude-sonnet-4-20250514 | ||
| temperature: 0.1 | ||
| tools: | ||
| write: false | ||
| edit: false | ||
| bash: false | ||
| permission: | ||
| bash: | ||
| "git diff*": allow | ||
| "git log*": allow | ||
| "grep *": allow | ||
| "*": deny | ||
| --- | ||
|
|
||
| You are a security expert. Identify vulnerabilities, check for OWASP Top 10 | ||
| issues, and verify proper input validation and output encoding. | ||
| ``` | ||
|
|
||
| ### JSON Agent (Global Config) | ||
|
|
||
| In `opencode.json`: | ||
|
|
||
| ```json | ||
| { | ||
| "agent": { | ||
| "security-reviewer": { | ||
| "description": "Security-focused code reviewer", | ||
| "mode": "subagent", | ||
| "model": "anthropic/claude-sonnet-4-20250514", | ||
| "tools": { "write": false, "edit": false } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Using Custom Agents | ||
|
|
||
| ```bash | ||
| # CLI | ||
| opencode run --agent security-reviewer "Audit the auth module" | ||
|
|
||
| # SDK | ||
| const result = await client.session.prompt({ | ||
| path: { id: session.id }, | ||
| body: { | ||
| agent: "security-reviewer", | ||
| parts: [{ type: "text", text: "Audit the auth module" }], | ||
| }, | ||
| }) | ||
| ``` | ||
|
|
||
| ## Model Provider Flexibility | ||
|
|
||
| OpenCode supports any provider via `opencode auth login`. Runners inherit the configured provider or override per-runner. | ||
|
|
||
| ```bash | ||
| # Configure providers | ||
| opencode auth login # Interactive provider selection | ||
|
|
||
| # Override model per dispatch | ||
| opencode run -m openrouter/anthropic/claude-sonnet-4-20250514 "Task" | ||
| opencode run -m groq/llama-4-scout-17b-16e-instruct "Quick task" | ||
| ``` | ||
|
|
||
| Environment variables for non-interactive setup: | ||
|
|
||
| ```bash | ||
| # Provider credentials (stored in ~/.local/share/opencode/auth.json) | ||
| opencode auth login | ||
|
|
||
| # Or set via environment | ||
| export ANTHROPIC_API_KEY="sk-ant-..." | ||
| export OPENAI_API_KEY="sk-..." | ||
| ``` | ||
|
|
||
| ## Security | ||
|
|
||
| 1. **Network**: Use `--hostname 127.0.0.1` (default) for local-only access | ||
| 2. **Auth**: Set `OPENCODE_SERVER_PASSWORD` when exposing to network | ||
| 3. **Permissions**: Use `OPENCODE_PERMISSION` env var for headless autonomy | ||
| 4. **Credentials**: Never pass secrets in prompts - use environment variables | ||
| 5. **Cleanup**: Delete sessions after use to prevent data leakage | ||
|
|
||
| ### Autonomous Mode (CI/CD) | ||
|
|
||
| ```bash | ||
| # Grant all permissions (only in trusted environments) | ||
| OPENCODE_PERMISSION='{"*":"allow"}' opencode run "Fix the failing tests" | ||
| ``` | ||
|
|
||
| ## CI/CD Integration | ||
|
|
||
| ### GitHub Actions | ||
|
|
||
| ```yaml | ||
| name: AI Code Review | ||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize] | ||
|
|
||
| jobs: | ||
| ai-review: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Install OpenCode | ||
| run: curl -fsSL https://opencode.ai/install | bash | ||
|
|
||
| - name: Run AI Review | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| OPENCODE_PERMISSION: '{"*":"allow"}' | ||
| run: | | ||
| opencode run --format json \ | ||
| "Review the changes in this PR for security and quality. Output as markdown." \ | ||
| > review.md | ||
| ``` | ||
|
|
||
| ## Related | ||
|
|
||
| - `tools/ai-assistants/opencode-server.md` - Full server API reference | ||
| - `tools/ai-assistants/overview.md` - AI assistant comparison | ||
| - `scripts/runner-helper.sh` - Runner management CLI | ||
| - `scripts/cron-dispatch.sh` - Cron-triggered dispatch | ||
| - `scripts/cron-helper.sh` - Cron job management | ||
| - `scripts/coordinator-helper.sh` - Multi-agent coordination | ||
| - `scripts/mail-helper.sh` - Inter-agent mailbox |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Shift to progressive disclosure + file:line pointers.
This doc embeds many long CLI/SDK/HTTP examples. Per the documentation guidelines, keep this file as a high‑level index and point to authoritative sources (scripts or subagent docs) using file:line references. Move the detailed examples into the owning implementation/doc and link here.
📎 Example pattern (replace inline blocks with pointers)
-```bash
-# One-shot task
-opencode run "Review src/auth.ts for security issues"
-...
-```
+See file:.agent/scripts/runner-helper.sh:259-417 for the canonical CLI dispatch flow,
+and tools/ai-assistants/opencode-server.md for API-specific examples.As per coding guidelines, .agent/**/*.md: Apply progressive disclosure pattern by using pointers to subagents rather than including inline content in agent documentation; include code examples only when authoritative; use file:line references to point to actual implementation instead of inline code snippets.
🤖 Prompt for AI Agents
In @.agent/tools/ai-assistants/headless-dispatch.md around lines 63 - 495, This
file contains lengthy inline CLI/SDK/HTTP examples under headings like "Dispatch
Methods" and "Parallel Execution"; replace those embedded blocks with
progressive-disclosure pointers to the canonical implementations (e.g.,
reference the dispatch flow in runner-helper.sh and API examples in
tools/ai-assistants/opencode-server.md using file:line style) and move the full
example content into the owning docs/scripts; apply this pattern across
`.agent/**/*.md` (remove large code fences under "Method 1/2/3/4", "Session
Management", "Parallel Execution", "Runners", and "CI/CD Integration" and
replace them with brief one-line pointers to the authoritative files you moved
the examples to).
| ```text | ||
| ~/.aidevops/.agent-workspace/runners/ | ||
| ├── code-reviewer/ | ||
| │ ├── AGENTS.md # Runner personality/instructions | ||
| │ ├── config.json # Runner configuration | ||
| │ └── memory.db # Runner-specific memories (optional) | ||
| └── seo-analyst/ | ||
| ├── AGENTS.md | ||
| ├── config.json | ||
| └── memory.db | ||
| ``` |
There was a problem hiding this comment.
Runner directory layout should match runner-helper outputs.
The structure omits session.id and runs/, which are used by the script. Aligning these avoids operator confusion.
🧩 Suggested alignment
~/.aidevops/.agent-workspace/runners/
├── code-reviewer/
│ ├── AGENTS.md # Runner personality/instructions
│ ├── config.json # Runner configuration
+│ ├── session.id # Last session ID (for --continue)
+│ └── runs/ # Run logs
│ └── memory.db # Runner-specific memories (optional)📝 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.
| ```text | |
| ~/.aidevops/.agent-workspace/runners/ | |
| ├── code-reviewer/ | |
| │ ├── AGENTS.md # Runner personality/instructions | |
| │ ├── config.json # Runner configuration | |
| │ └── memory.db # Runner-specific memories (optional) | |
| └── seo-analyst/ | |
| ├── AGENTS.md | |
| ├── config.json | |
| └── memory.db | |
| ``` |
🤖 Prompt for AI Agents
In @.agent/tools/ai-assistants/headless-dispatch.md around lines 259 - 269,
Update the runner directory example so it matches the runner-helper script
output by adding the missing session.id file and runs/ directory to the
displayed tree (i.e., include "session.id" and a "runs/" entry under each runner
like code-reviewer and seo-analyst); edit the tree in the
.agent/tools/ai-assistants/headless-dispatch.md example block (the snippet
showing ~/.aidevops/.agent-workspace/runners/) so operators see the exact layout
produced by the script.
| **Status:** In Progress (Phase 2/5) | ||
| **Estimate:** ~3d (ai:1.5d test:1d read:0.5d) | ||
| **Source:** [alexfazio's X post on droids](https://gist.github.com/alexfazio/dcf2f253d346d8ed2702935b57184582) | ||
|
|
||
| <!--TOON:plan{id,title,status,phase,total_phases,owner,tags,est,est_ai,est_test,est_read,logged,started}: | ||
| p016,Parallel Agents & Headless Dispatch,planning,0,5,,agents|parallel|headless|dispatch|matrix|memory,3d,1.5d,1d,0.5d,2026-02-03T00:00Z, | ||
| p016,Parallel Agents & Headless Dispatch,in_progress,2,5,,agents|parallel|headless|dispatch|runners|memory,3d,1.5d,1d,0.5d,2026-02-03T00:00Z,2026-02-05T00:00Z | ||
| --> |
There was a problem hiding this comment.
TOON IDs collide with existing plans/milestones/decisions.
p016 is already used earlier (Install Script Integrity Hardening), and m064/d039 are already used in other plans. Duplicate IDs break TOON indexing and automation. Please renumber this plan block to the next available IDs and update its milestones/decisions consistently. Also align milestone naming with the runner rename (still says droid-helper.sh).
🔁 Suggested renumbering pattern
-<!--TOON:plan{id,title,status,phase,total_phases,owner,tags,est,est_ai,est_test,est_read,logged,started}:
-p016,Parallel Agents & Headless Dispatch,in_progress,2,5,,agents|parallel|headless|dispatch|runners|memory,3d,1.5d,1d,0.5d,2026-02-03T00:00Z,2026-02-05T00:00Z
--->
+<!--TOON:plan{id,title,status,phase,total_phases,owner,tags,est,est_ai,est_test,est_read,logged,started}:
+p019,Parallel Agents & Headless Dispatch,in_progress,2,5,,agents|parallel|headless|dispatch|runners|memory,3d,1.5d,1d,0.5d,2026-02-03T00:00Z,2026-02-05T00:00Z
+-->
@@
-<!--TOON:milestones[5]{id,plan_id,desc,est,actual,scheduled,completed,status}:
-m064,p016,Phase 1: Document headless dispatch patterns,4h,,2026-02-03T00:00Z,,pending
-m065,p016,Phase 2: Create droid-helper.sh,4h,,2026-02-03T00:00Z,,pending
+<!--TOON:milestones[5]{id,plan_id,desc,est,actual,scheduled,completed,status}:
+m095,p019,Phase 1: Document headless dispatch patterns,4h,,2026-02-03T00:00Z,,pending
+m096,p019,Phase 2: Create runner-helper.sh,4h,,2026-02-03T00:00Z,,pending
@@
-<!--TOON:decisions[4]{id,plan_id,decision,rationale,date,impact}:
-d039,p016,Local parallel sessions over containers/cloud,Zero hosting cost shared filesystem no sync needed,2026-02-03,Architecture
+<!--TOON:decisions[4]{id,plan_id,decision,rationale,date,impact}:
+d047,p019,Local parallel sessions over containers/cloud,Zero hosting cost shared filesystem no sync needed,2026-02-03,ArchitectureAlso applies to: 2080-2085, 2106-2109
🤖 Prompt for AI Agents
In `@todo/PLANS.md` around lines 1974 - 1980, The TOON plan block uses duplicate
IDs (p016, m064, d039) which collide with existing entries; update the plan
block's identifiers to the next free TOON IDs (e.g., replace p016 with a new
p### and update its related milestone/decision IDs m###/d###) and ensure the
same renumbering is applied to the other affected blocks (lines referenced in
the comment: 2080-2085 and 2106-2109) so all TOON tags are unique; also rename
any milestone/decision text that still references "droid-helper.sh" to the new
runner name to keep naming consistent (locate the TOON marker comment starting
with "<!--TOON:plan" and update IDs and text inside that block plus its matching
m/d entries).



Summary
tools/ai-assistants/headless-dispatch.md- comprehensive guide for OpenCode headless dispatch patterns includingopencode run,opencode serve,--attachwarm server, SDK parallel dispatch, custom agents, and CI/CD integrationrunner-helper.sh- named headless AI agent instances with create/run/status/list/edit/logs/stop/destroy commandsChanges
.agent/tools/ai-assistants/headless-dispatch.md.agent/scripts/runner-helper.shTODO.mdtodo/PLANS.md.agent/subagent-index.toonREADME.mdQuality
Summary by CodeRabbit
New Features
Documentation