#!/usr/bin/env bash set -eo pipefail # shellcheck disable=SC1090 source "$LLM_PROMPT_UTILS_FILE" source "$LLM_ROOT_DIR/agents/.shared/utils.sh" # @env LLM_OUTPUT=/dev/stdout # @env LLM_AGENT_VAR_PROJECT_DIR=. # @describe Code review orchestrator tools _project_dir() { local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}" (cd "${dir}" 2>/dev/null && pwd) || echo "${dir}" } # @cmd Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. # @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA) get_diff() { local project_dir project_dir=$(_project_dir) # shellcheck disable=SC2154 local base="${argc_base:-}" local diff_output="" if [[ -n "${base}" ]]; then diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true else diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true if [[ -z "${diff_output}" ]]; then diff_output=$(cd "${project_dir}" && git diff 2>&1) || true fi if [[ -z "${diff_output}" ]]; then diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true fi fi if [[ -z "${diff_output}" ]]; then warn "No changes found to review" >> "$LLM_OUTPUT" return 0 fi local file_count file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true) { info "Diff contains changes to ${file_count} file(s)" echo "" echo "${diff_output}" } >> "$LLM_OUTPUT" } # @cmd Get list of changed files with stats # @option --base Optional base ref to diff against get_changed_files() { local project_dir project_dir=$(_project_dir) local base="${argc_base:-}" local stat_output="" if [[ -n "${base}" ]]; then stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true else stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true if [[ -z "${stat_output}" ]]; then stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true fi if [[ -z "${stat_output}" ]]; then stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true fi fi if [[ -z "${stat_output}" ]]; then warn "No changes found" >> "$LLM_OUTPUT" return 0 fi { info "Changed files:" echo "" echo "${stat_output}" } >> "$LLM_OUTPUT" } # @cmd Get project structure and type information get_project_info() { local project_dir project_dir=$(_project_dir) local project_info project_info=$(detect_project "${project_dir}") { info "Project: ${project_dir}" echo "Type: $(echo "${project_info}" | jq -r '.type')" echo "" get_tree "${project_dir}" 2 } >> "$LLM_OUTPUT" } # ARGC-BUILD { # This block was generated by argc (https://github.com/sigoden/argc). # Modifying it manually is not recommended _argc_run() { if [[ "${1:-}" == "___internal___" ]]; then _argc_die "error: unsupported ___internal___ command" fi if [[ "${OS:-}" == "Windows_NT" ]] && [[ -n "${MSYSTEM:-}" ]]; then set -o igncr fi argc__args=("$(basename "$0" .sh)" "$@") argc__positionals=() _argc_index=1 _argc_len="${#argc__args[@]}" _argc_tools=() _argc_parse if [ -n "${argc__fn:-}" ]; then $argc__fn "${argc__positionals[@]}" fi } _argc_usage() { cat <<-'EOF' Code review orchestrator tools USAGE: COMMANDS: get_diff Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. [aliases: get-diff] get_changed_files Get list of changed files with stats [aliases: get-changed-files] get_project_info Get project structure and type information [aliases: get-project-info] ENVIRONMENTS: LLM_OUTPUT [default: /dev/stdout] LLM_AGENT_VAR_PROJECT_DIR [default: .] EOF exit } _argc_version() { echo 0.0.0 exit } _argc_parse() { local _argc_key _argc_action local _argc_subcmds="get_diff, get-diff, get_changed_files, get-changed-files, get_project_info, get-project-info" while [[ $_argc_index -lt $_argc_len ]]; do _argc_item="${argc__args[_argc_index]}" _argc_key="${_argc_item%%=*}" case "$_argc_key" in --help | -help | -h) _argc_usage ;; --version | -version | -V) _argc_version ;; --) _argc_dash="${#argc__positionals[@]}" argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") _argc_index=$_argc_len break ;; get_diff | get-diff) _argc_index=$((_argc_index + 1)) _argc_action=_argc_parse_get_diff break ;; get_changed_files | get-changed-files) _argc_index=$((_argc_index + 1)) _argc_action=_argc_parse_get_changed_files break ;; get_project_info | get-project-info) _argc_index=$((_argc_index + 1)) _argc_action=_argc_parse_get_project_info break ;; help) local help_arg="${argc__args[$((_argc_index + 1))]:-}" case "$help_arg" in get_diff | get-diff) _argc_usage_get_diff ;; get_changed_files | get-changed-files) _argc_usage_get_changed_files ;; get_project_info | get-project-info) _argc_usage_get_project_info ;; "") _argc_usage ;; *) _argc_die "error: invalid value \`$help_arg\` for \`\`"$'\n'" [possible values: $_argc_subcmds]" ;; esac ;; *) _argc_die "error: \`\` requires a subcommand but one was not provided"$'\n'" [subcommands: $_argc_subcmds]" ;; esac done if [[ -n "${_argc_action:-}" ]]; then $_argc_action else _argc_usage fi } _argc_usage_get_diff() { cat <<-'EOF' Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. USAGE: get_diff [OPTIONS] OPTIONS: --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA) -h, --help Print help ENVIRONMENTS: LLM_OUTPUT [default: /dev/stdout] LLM_AGENT_VAR_PROJECT_DIR [default: .] EOF exit } _argc_parse_get_diff() { local _argc_key _argc_action local _argc_subcmds="" while [[ $_argc_index -lt $_argc_len ]]; do _argc_item="${argc__args[_argc_index]}" _argc_key="${_argc_item%%=*}" case "$_argc_key" in --help | -help | -h) _argc_usage_get_diff ;; --) _argc_dash="${#argc__positionals[@]}" argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") _argc_index=$_argc_len break ;; --base) _argc_take_args "--base " 1 1 "-" "" _argc_index=$((_argc_index + _argc_take_args_len + 1)) if [[ -z "${argc_base:-}" ]]; then argc_base="${_argc_take_args_values[0]:-}" else _argc_die "error: the argument \`--base\` cannot be used multiple times" fi ;; *) if _argc_maybe_flag_option "-" "$_argc_item"; then _argc_die "error: unexpected argument \`$_argc_key\` found" fi argc__positionals+=("$_argc_item") _argc_index=$((_argc_index + 1)) ;; esac done if [[ -n "${_argc_action:-}" ]]; then $_argc_action else argc__fn=get_diff if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then _argc_usage_get_diff fi if [[ -z "${LLM_OUTPUT:-}" ]]; then export LLM_OUTPUT=/dev/stdout fi if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then export LLM_AGENT_VAR_PROJECT_DIR=. fi fi } _argc_usage_get_changed_files() { cat <<-'EOF' Get list of changed files with stats USAGE: get_changed_files [OPTIONS] OPTIONS: --base Optional base ref to diff against -h, --help Print help ENVIRONMENTS: LLM_OUTPUT [default: /dev/stdout] LLM_AGENT_VAR_PROJECT_DIR [default: .] EOF exit } _argc_parse_get_changed_files() { local _argc_key _argc_action local _argc_subcmds="" while [[ $_argc_index -lt $_argc_len ]]; do _argc_item="${argc__args[_argc_index]}" _argc_key="${_argc_item%%=*}" case "$_argc_key" in --help | -help | -h) _argc_usage_get_changed_files ;; --) _argc_dash="${#argc__positionals[@]}" argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") _argc_index=$_argc_len break ;; --base) _argc_take_args "--base " 1 1 "-" "" _argc_index=$((_argc_index + _argc_take_args_len + 1)) if [[ -z "${argc_base:-}" ]]; then argc_base="${_argc_take_args_values[0]:-}" else _argc_die "error: the argument \`--base\` cannot be used multiple times" fi ;; *) if _argc_maybe_flag_option "-" "$_argc_item"; then _argc_die "error: unexpected argument \`$_argc_key\` found" fi argc__positionals+=("$_argc_item") _argc_index=$((_argc_index + 1)) ;; esac done if [[ -n "${_argc_action:-}" ]]; then $_argc_action else argc__fn=get_changed_files if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then _argc_usage_get_changed_files fi if [[ -z "${LLM_OUTPUT:-}" ]]; then export LLM_OUTPUT=/dev/stdout fi if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then export LLM_AGENT_VAR_PROJECT_DIR=. fi fi } _argc_usage_get_project_info() { cat <<-'EOF' Get project structure and type information USAGE: get_project_info ENVIRONMENTS: LLM_OUTPUT [default: /dev/stdout] LLM_AGENT_VAR_PROJECT_DIR [default: .] EOF exit } _argc_parse_get_project_info() { local _argc_key _argc_action local _argc_subcmds="" while [[ $_argc_index -lt $_argc_len ]]; do _argc_item="${argc__args[_argc_index]}" _argc_key="${_argc_item%%=*}" case "$_argc_key" in --help | -help | -h) _argc_usage_get_project_info ;; --) _argc_dash="${#argc__positionals[@]}" argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}") _argc_index=$_argc_len break ;; *) argc__positionals+=("$_argc_item") _argc_index=$((_argc_index + 1)) ;; esac done if [[ -n "${_argc_action:-}" ]]; then $_argc_action else argc__fn=get_project_info if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then _argc_usage_get_project_info fi if [[ -z "${LLM_OUTPUT:-}" ]]; then export LLM_OUTPUT=/dev/stdout fi if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then export LLM_AGENT_VAR_PROJECT_DIR=. fi fi } _argc_take_args() { _argc_take_args_values=() _argc_take_args_len=0 local param="$1" min="$2" max="$3" signs="$4" delimiter="$5" if [[ "$min" -eq 0 ]] && [[ "$max" -eq 0 ]]; then return fi local _argc_take_index=$((_argc_index + 1)) _argc_take_value if [[ "$_argc_item" == *=* ]]; then _argc_take_args_values=("${_argc_item##*=}") else while [[ $_argc_take_index -lt $_argc_len ]]; do _argc_take_value="${argc__args[_argc_take_index]}" if _argc_maybe_flag_option "$signs" "$_argc_take_value"; then if [[ "${#_argc_take_value}" -gt 1 ]]; then break fi fi _argc_take_args_values+=("$_argc_take_value") _argc_take_args_len=$((_argc_take_args_len + 1)) if [[ "$_argc_take_args_len" -ge "$max" ]]; then break fi _argc_take_index=$((_argc_take_index + 1)) done fi if [[ "${#_argc_take_args_values[@]}" -lt "$min" ]]; then _argc_die "error: incorrect number of values for \`$param\`" fi if [[ -n "$delimiter" ]] && [[ "${#_argc_take_args_values[@]}" -gt 0 ]]; then local item values arr=() for item in "${_argc_take_args_values[@]}"; do IFS="$delimiter" read -r -a values <<<"$item" arr+=("${values[@]}") done _argc_take_args_values=("${arr[@]}") fi } _argc_maybe_flag_option() { local signs="$1" arg="$2" if [[ -z "$signs" ]]; then return 1 fi local cond=false if [[ "$signs" == *"+"* ]]; then if [[ "$arg" =~ ^\+[^+].* ]]; then cond=true fi elif [[ "$arg" == -* ]]; then if (( ${#arg} < 3 )) || [[ ! "$arg" =~ ^---.* ]]; then cond=true fi fi if [[ "$cond" == "false" ]]; then return 1 fi local value="${arg%%=*}" if [[ "$value" =~ [[:space:]] ]]; then return 1 fi return 0 } _argc_die() { if [[ $# -eq 0 ]]; then cat else echo "$*" >&2 fi exit 1 } _argc_run "$@" # ARGC-BUILD }