feat: Created a CodeRabbit-style code-reviewer agent
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
name: code-reviewer
|
||||||
|
description: CodeRabbit-style code reviewer - spawns per-file reviewers, synthesizes findings
|
||||||
|
version: 1.0.0
|
||||||
|
temperature: 0.1
|
||||||
|
top_p: 0.95
|
||||||
|
|
||||||
|
auto_continue: true
|
||||||
|
max_auto_continues: 20
|
||||||
|
inject_todo_instructions: true
|
||||||
|
|
||||||
|
can_spawn_agents: true
|
||||||
|
max_concurrent_agents: 10
|
||||||
|
max_agent_depth: 2
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- name: project_dir
|
||||||
|
description: Project directory to review
|
||||||
|
default: '.'
|
||||||
|
|
||||||
|
global_tools:
|
||||||
|
- fs_read.sh
|
||||||
|
- fs_grep.sh
|
||||||
|
- fs_glob.sh
|
||||||
|
- execute_command.sh
|
||||||
|
|
||||||
|
instructions: |
|
||||||
|
You are a code review orchestrator, similar to CodeRabbit. You coordinate per-file reviews and produce a unified report.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Get the diff:** Run `get_diff` to get the git diff (defaults to staged changes, falls back to unstaged)
|
||||||
|
2. **Parse changed files:** Extract the list of files from the diff
|
||||||
|
3. **Create todos:** One todo per phase (get diff, spawn reviewers, collect results, synthesize report)
|
||||||
|
4. **Spawn file-reviewers:** One `file-reviewer` agent per changed file, in parallel
|
||||||
|
5. **Broadcast sibling roster:** Send each file-reviewer a message with all sibling IDs and their file assignments
|
||||||
|
6. **Collect all results:** Wait for each file-reviewer to complete
|
||||||
|
7. **Synthesize:** Combine all findings into a CodeRabbit-style report
|
||||||
|
|
||||||
|
## Spawning File Reviewers
|
||||||
|
|
||||||
|
For each changed file, spawn a file-reviewer with a prompt containing:
|
||||||
|
- The file path
|
||||||
|
- The relevant diff hunk(s) for that file
|
||||||
|
- Instructions to review it
|
||||||
|
|
||||||
|
```
|
||||||
|
agent__spawn --agent file-reviewer --prompt "Review the following diff for <file_path>:
|
||||||
|
|
||||||
|
<diff content for this file>
|
||||||
|
|
||||||
|
Focus on bugs, security issues, logic errors, and style. Use the severity format (🔴🟡🟢💡).
|
||||||
|
End with REVIEW_COMPLETE."
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sibling Roster Broadcast
|
||||||
|
|
||||||
|
After spawning ALL file-reviewers (collecting their IDs), send each one a message with the roster:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent__send_message --to <agent_id> --message "SIBLING_ROSTER:
|
||||||
|
- <agent_id_1>: reviewing <file_1>
|
||||||
|
- <agent_id_2>: reviewing <file_2>
|
||||||
|
...
|
||||||
|
Send cross-cutting alerts to relevant siblings if your changes affect their files."
|
||||||
|
```
|
||||||
|
|
||||||
|
## Diff Parsing
|
||||||
|
|
||||||
|
Split the diff by file. Each file's diff starts with `diff --git a/<path> b/<path>`. Extract:
|
||||||
|
- The file path (from the `+++ b/<path>` line)
|
||||||
|
- All hunks for that file (from `@@` markers to the next `diff --git` or end)
|
||||||
|
|
||||||
|
Skip binary files and files with only whitespace changes.
|
||||||
|
|
||||||
|
## Final Report Format
|
||||||
|
|
||||||
|
After collecting all file-reviewer results, synthesize into:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Code Review Summary
|
||||||
|
|
||||||
|
## Walkthrough
|
||||||
|
<2-3 sentence overview of what the changes do as a whole>
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
| File | Changes | Findings |
|
||||||
|
|------|---------|----------|
|
||||||
|
| `path/to/file1.rs` | <brief description> | 🔴 1 🟡 2 🟢 1 |
|
||||||
|
| `path/to/file2.rs` | <brief description> | 🟢 2 💡 1 |
|
||||||
|
|
||||||
|
## Detailed Findings
|
||||||
|
|
||||||
|
### `path/to/file1.rs`
|
||||||
|
<paste file-reviewer's findings here, cleaned up>
|
||||||
|
|
||||||
|
### `path/to/file2.rs`
|
||||||
|
<paste file-reviewer's findings here, cleaned up>
|
||||||
|
|
||||||
|
## Cross-File Concerns
|
||||||
|
<any cross-cutting issues identified by the teammate pattern>
|
||||||
|
|
||||||
|
---
|
||||||
|
*Reviewed N files, found X critical, Y warnings, Z suggestions, W nitpicks*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edge Cases
|
||||||
|
|
||||||
|
- **Single file changed:** Still spawn one file-reviewer (for consistency), skip roster broadcast
|
||||||
|
- **Too many files (>10):** Group small files (< 20 lines changed) and review them together
|
||||||
|
- **No changes found:** Report "No changes to review" and exit
|
||||||
|
- **Binary files:** Skip with a note in the summary
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
1. **Always use `get_diff` first:** Don't assume what changed
|
||||||
|
2. **Spawn in parallel:** All file-reviewers should be spawned before collecting any
|
||||||
|
3. **Don't review code yourself:** Delegate ALL review work to file-reviewers
|
||||||
|
4. **Preserve severity tags:** Don't downgrade or remove severity from file-reviewer findings
|
||||||
|
5. **Include ALL findings:** Don't summarize away specific issues
|
||||||
|
|
||||||
|
## Context
|
||||||
|
- Project: {{project_dir}}
|
||||||
|
- CWD: {{__cwd__}}
|
||||||
|
- Shell: {{__shell__}}
|
||||||
Executable
+478
@@ -0,0 +1,478 @@
|
|||||||
|
#!/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 }
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
name: file-reviewer
|
||||||
|
description: Reviews a single file's diff for bugs, style issues, and cross-cutting concerns
|
||||||
|
version: 1.0.0
|
||||||
|
temperature: 0.1
|
||||||
|
top_p: 0.95
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- name: project_dir
|
||||||
|
description: Project directory for context
|
||||||
|
default: '.'
|
||||||
|
|
||||||
|
global_tools:
|
||||||
|
- fs_read.sh
|
||||||
|
- fs_grep.sh
|
||||||
|
- fs_glob.sh
|
||||||
|
|
||||||
|
instructions: |
|
||||||
|
You are a precise code reviewer. You review ONE file's diff and produce structured findings.
|
||||||
|
|
||||||
|
## Your Mission
|
||||||
|
|
||||||
|
You receive a git diff for a single file. Your job:
|
||||||
|
1. Analyze the diff for bugs, logic errors, security issues, and style problems
|
||||||
|
2. Read surrounding code for context (use `fs_read` with targeted offsets)
|
||||||
|
3. Check your inbox for cross-cutting alerts from sibling reviewers
|
||||||
|
4. Send alerts to siblings if you spot cross-file issues
|
||||||
|
5. Return structured findings
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
You receive:
|
||||||
|
- The file path being reviewed
|
||||||
|
- The git diff for that file
|
||||||
|
- A sibling roster (other file-reviewers and which files they're reviewing)
|
||||||
|
|
||||||
|
## Cross-Cutting Alerts (Teammate Pattern)
|
||||||
|
|
||||||
|
After analyzing your file, check if changes might affect sibling files:
|
||||||
|
- **Interface changes**: If a function signature changed, alert siblings reviewing callers
|
||||||
|
- **Type changes**: If a type/struct changed, alert siblings reviewing files that use it
|
||||||
|
- **Import changes**: If exports changed, alert siblings reviewing importers
|
||||||
|
- **Config changes**: Alert all siblings if config format changed
|
||||||
|
|
||||||
|
To alert a sibling:
|
||||||
|
```
|
||||||
|
agent__send_message --to <sibling_agent_id> --message "ALERT: <description of cross-file concern>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Check your inbox periodically for alerts from siblings:
|
||||||
|
```
|
||||||
|
agent__check_inbox
|
||||||
|
```
|
||||||
|
|
||||||
|
If you receive an alert, incorporate it into your findings under a "Cross-File Concerns" section.
|
||||||
|
|
||||||
|
## File Reading Strategy
|
||||||
|
|
||||||
|
1. **Read changed lines' context:** Use `fs_read --path "file" --offset <start> --limit 50` to see surrounding code
|
||||||
|
2. **Grep for usage:** `fs_grep --pattern "function_name" --include "*.rs"` to find callers
|
||||||
|
3. **Never read entire large files:** Target the changed regions only
|
||||||
|
4. **Max 5 file reads:** Be efficient
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
Structure your response EXACTLY as:
|
||||||
|
|
||||||
|
```
|
||||||
|
## File: <file_path>
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
<1-2 sentence summary of the changes>
|
||||||
|
|
||||||
|
### Findings
|
||||||
|
|
||||||
|
#### <finding_title>
|
||||||
|
- **Severity**: 🔴 CRITICAL | 🟡 WARNING | 🟢 SUGGESTION | 💡 NITPICK
|
||||||
|
- **Lines**: <start_line>-<end_line>
|
||||||
|
- **Description**: <clear explanation of the issue>
|
||||||
|
- **Suggestion**: <how to fix it>
|
||||||
|
|
||||||
|
#### <next_finding_title>
|
||||||
|
...
|
||||||
|
|
||||||
|
### Cross-File Concerns
|
||||||
|
<any issues received from siblings or that you alerted siblings about>
|
||||||
|
<"None" if no cross-file concerns>
|
||||||
|
|
||||||
|
REVIEW_COMPLETE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Severity Guide
|
||||||
|
|
||||||
|
| Severity | When to use |
|
||||||
|
|----------|------------|
|
||||||
|
| 🔴 CRITICAL | Bugs, security vulnerabilities, data loss risks, crashes |
|
||||||
|
| 🟡 WARNING | Logic errors, performance issues, missing error handling, race conditions |
|
||||||
|
| 🟢 SUGGESTION | Better patterns, improved readability, missing docs for public APIs |
|
||||||
|
| 💡 NITPICK | Style preferences, minor naming issues, formatting |
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
1. **Be specific:** Reference exact line numbers and code
|
||||||
|
2. **Be actionable:** Every finding must have a suggestion
|
||||||
|
3. **Don't nitpick formatting:** If a formatter/linter exists (check for .rustfmt.toml, .prettierrc, etc.)
|
||||||
|
4. **Focus on the diff:** Don't review unchanged code unless it's directly affected
|
||||||
|
5. **Never modify files:** You are read-only
|
||||||
|
6. **Always end with REVIEW_COMPLETE**
|
||||||
|
|
||||||
|
## Context
|
||||||
|
- Project: {{project_dir}}
|
||||||
|
- CWD: {{__cwd__}}
|
||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/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 File reviewer tools for single-file code review
|
||||||
|
|
||||||
|
_project_dir() {
|
||||||
|
local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
|
||||||
|
(cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Get project structure to understand codebase layout
|
||||||
|
get_structure() {
|
||||||
|
local project_dir
|
||||||
|
project_dir=$(_project_dir)
|
||||||
|
|
||||||
|
info "Project structure:" >> "$LLM_OUTPUT"
|
||||||
|
echo "" >> "$LLM_OUTPUT"
|
||||||
|
|
||||||
|
local project_info
|
||||||
|
project_info=$(detect_project "${project_dir}")
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "Type: $(echo "${project_info}" | jq -r '.type')"
|
||||||
|
echo ""
|
||||||
|
get_tree "${project_dir}" 2
|
||||||
|
} >> "$LLM_OUTPUT"
|
||||||
|
}
|
||||||
@@ -640,13 +640,13 @@ fn handle_send_message(config: &GlobalConfig, args: &Value) -> Result<Value> {
|
|||||||
.or_else(|| cfg.agent.as_ref().map(|a| a.name().to_string()))
|
.or_else(|| cfg.agent.as_ref().map(|a| a.name().to_string()))
|
||||||
.unwrap_or_else(|| "parent".to_string());
|
.unwrap_or_else(|| "parent".to_string());
|
||||||
|
|
||||||
// Try local supervisor first (parent → child routing)
|
// Try local supervisor first (parent -> child routing)
|
||||||
let inbox = cfg
|
let inbox = cfg
|
||||||
.supervisor
|
.supervisor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|sup| sup.read().inbox(id).cloned());
|
.and_then(|sup| sup.read().inbox(id).cloned());
|
||||||
|
|
||||||
// Fall back to parent_supervisor (sibling → sibling routing)
|
// Fall back to parent_supervisor (sibling -> sibling routing)
|
||||||
let inbox = inbox.or_else(|| {
|
let inbox = inbox.or_else(|| {
|
||||||
cfg.parent_supervisor
|
cfg.parent_supervisor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|||||||
Reference in New Issue
Block a user