478 lines
14 KiB
Bash
Executable File
478 lines
14 KiB
Bash
Executable File
#!/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: <COMMAND>
|
|
|
|
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 \`<command>\`"$'\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 <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 <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 <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 <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 } |