148 lines
3.5 KiB
Bash
Executable File
148 lines
3.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# OpenCode driver for Ralph
|
|
# Uses OpenCode's build agent with JSON event output and optional session resume.
|
|
|
|
driver_name() {
|
|
echo "opencode"
|
|
}
|
|
|
|
driver_display_name() {
|
|
echo "OpenCode"
|
|
}
|
|
|
|
driver_cli_binary() {
|
|
echo "opencode"
|
|
}
|
|
|
|
driver_min_version() {
|
|
echo "0.1.0"
|
|
}
|
|
|
|
driver_check_available() {
|
|
command -v "$(driver_cli_binary)" &>/dev/null
|
|
}
|
|
|
|
driver_valid_tools() {
|
|
VALID_TOOL_PATTERNS=(
|
|
"bash"
|
|
"read"
|
|
"write"
|
|
"edit"
|
|
"grep"
|
|
"question"
|
|
)
|
|
}
|
|
|
|
driver_supports_tool_allowlist() {
|
|
return 1
|
|
}
|
|
|
|
driver_permission_denial_help() {
|
|
echo " - $DRIVER_DISPLAY_NAME uses its native permission and approval model."
|
|
echo " - ALLOWED_TOOLS in $RALPHRC_FILE is ignored for this driver."
|
|
echo " - BMAD workflows can use OpenCode's native question tool when needed."
|
|
echo " - Review OpenCode permissions, then restart the loop."
|
|
}
|
|
|
|
driver_build_command() {
|
|
local prompt_file=$1
|
|
local loop_context=$2
|
|
local session_id=$3
|
|
|
|
CLAUDE_CMD_ARGS=("$(driver_cli_binary)" "run" "--agent" "build" "--format" "json")
|
|
|
|
if [[ ! -f "$prompt_file" ]]; then
|
|
echo "ERROR: Prompt file not found: $prompt_file" >&2
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$CLAUDE_USE_CONTINUE" == "true" && -n "$session_id" ]]; then
|
|
CLAUDE_CMD_ARGS+=("--continue" "--session" "$session_id")
|
|
fi
|
|
|
|
local prompt_content
|
|
prompt_content=$(cat "$prompt_file")
|
|
if [[ -n "$loop_context" ]]; then
|
|
prompt_content="$loop_context
|
|
|
|
$prompt_content"
|
|
fi
|
|
|
|
CLAUDE_CMD_ARGS+=("$prompt_content")
|
|
}
|
|
|
|
driver_supports_sessions() {
|
|
return 0
|
|
}
|
|
|
|
driver_supports_live_output() {
|
|
return 0
|
|
}
|
|
|
|
driver_prepare_live_command() {
|
|
LIVE_CMD_ARGS=("${CLAUDE_CMD_ARGS[@]}")
|
|
}
|
|
|
|
driver_stream_filter() {
|
|
echo 'select((.type == "message.updated" or .type == "message.completed") and .message.role == "assistant") | ([.message.parts[]? | select(.type == "text") | .text] | join("\n"))'
|
|
}
|
|
|
|
driver_extract_session_id_from_output() {
|
|
local output_file=$1
|
|
|
|
if [[ ! -f "$output_file" ]]; then
|
|
echo ""
|
|
return 1
|
|
fi
|
|
|
|
local session_id
|
|
session_id=$(sed -n 's/.*"session"[^{]*{[^}]*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$output_file" | head -n 1 | tr -d '\r')
|
|
|
|
echo "$session_id"
|
|
[[ -n "$session_id" && "$session_id" != "null" ]]
|
|
}
|
|
|
|
driver_fallback_session_id() {
|
|
local cli_binary
|
|
cli_binary=$(driver_cli_binary)
|
|
|
|
local sessions_json
|
|
sessions_json=$("$cli_binary" session list --format json 2>/dev/null) || {
|
|
echo ""
|
|
return 1
|
|
}
|
|
|
|
local session_ids
|
|
if command -v jq >/dev/null 2>&1; then
|
|
session_ids=$(printf '%s' "$sessions_json" | jq -r '
|
|
if type == "array" then
|
|
[.[]?.id // empty]
|
|
elif (.sessions? | type) == "array" then
|
|
[.sessions[]?.id // empty]
|
|
else
|
|
[]
|
|
end
|
|
| map(select(length > 0))
|
|
| .[]
|
|
' 2>/dev/null | tr -d '\r')
|
|
else
|
|
session_ids=$(printf '%s' "$sessions_json" | grep -oE '"id"[[:space:]]*:[[:space:]]*"[^"]+"' | sed 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)"/\1/' | tr -d '\r')
|
|
fi
|
|
|
|
local -a session_id_candidates=()
|
|
local session_id
|
|
while IFS= read -r session_id; do
|
|
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
|
session_id_candidates+=("$session_id")
|
|
fi
|
|
done <<< "$session_ids"
|
|
|
|
if [[ ${#session_id_candidates[@]} -ne 1 ]]; then
|
|
echo ""
|
|
return 1
|
|
fi
|
|
|
|
echo "${session_id_candidates[0]}"
|
|
return 0
|
|
}
|