mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why `codex_core` is consistently a bottleneck for incremental builds during iteration. The simplest fix is to make the crate smaller. ## Summary `codex-core` owns several reusable prompt renderers and static prompt assets, which makes the crate harder to split apart. Rename `codex-review-prompts` to `codex-prompts` and move shared review, goal, permissions, compaction, realtime, hierarchical AGENTS.md, and `apply_patch` prompts into it. Move prompt-only tests and update consumers and `CODEOWNERS`. ## Validation - `just test -p codex-prompts -p codex-apply-patch` - `just test -p codex-core prompt_caching` - Bazel builds for the affected crates
138 lines
5.5 KiB
Rust
138 lines
5.5 KiB
Rust
use codex_git_utils::merge_base_with_head;
|
|
use codex_protocol::protocol::ReviewRequest;
|
|
use codex_protocol::protocol::ReviewTarget;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_template::Template;
|
|
use std::sync::LazyLock;
|
|
|
|
/// Review thread system prompt.
|
|
pub const REVIEW_PROMPT: &str = include_str!("../templates/review/rubric.md");
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct ResolvedReviewRequest {
|
|
pub target: ReviewTarget,
|
|
pub prompt: String,
|
|
pub user_facing_hint: String,
|
|
}
|
|
|
|
const UNCOMMITTED_PROMPT: &str = "Review the current code changes (staged, unstaged, and untracked files) and provide prioritized findings.";
|
|
|
|
const BASE_BRANCH_PROMPT_BACKUP: &str = "Review the code changes against the base branch '{{branch}}'. Start by finding the merge diff between the current branch and {{branch}}'s upstream e.g. (`git merge-base HEAD \"$(git rev-parse --abbrev-ref \"{{branch}}@{upstream}\")\"`), then run `git diff` against that SHA to see what changes we would merge into the {{branch}} branch. Provide prioritized, actionable findings.";
|
|
const BASE_BRANCH_PROMPT: &str = "Review the code changes against the base branch '{{base_branch}}'. The merge base commit for this comparison is {{merge_base_sha}}. Run `git diff {{merge_base_sha}}` to inspect the changes relative to {{base_branch}}. Provide prioritized, actionable findings.";
|
|
static BASE_BRANCH_PROMPT_BACKUP_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
|
Template::parse(BASE_BRANCH_PROMPT_BACKUP)
|
|
.unwrap_or_else(|err| panic!("base branch backup review prompt must parse: {err}"))
|
|
});
|
|
static BASE_BRANCH_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
|
Template::parse(BASE_BRANCH_PROMPT)
|
|
.unwrap_or_else(|err| panic!("base branch review prompt must parse: {err}"))
|
|
});
|
|
|
|
const COMMIT_PROMPT_WITH_TITLE: &str = "Review the code changes introduced by commit {{sha}} (\"{{title}}\"). Provide prioritized, actionable findings.";
|
|
const COMMIT_PROMPT: &str = "Review the code changes introduced by commit {{sha}}. Provide prioritized, actionable findings.";
|
|
static COMMIT_PROMPT_WITH_TITLE_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
|
Template::parse(COMMIT_PROMPT_WITH_TITLE)
|
|
.unwrap_or_else(|err| panic!("commit review prompt with title must parse: {err}"))
|
|
});
|
|
static COMMIT_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
|
Template::parse(COMMIT_PROMPT)
|
|
.unwrap_or_else(|err| panic!("commit review prompt must parse: {err}"))
|
|
});
|
|
|
|
pub fn resolve_review_request(
|
|
request: ReviewRequest,
|
|
cwd: &AbsolutePathBuf,
|
|
) -> anyhow::Result<ResolvedReviewRequest> {
|
|
let target = request.target;
|
|
let prompt = review_prompt(&target, cwd)?;
|
|
let user_facing_hint = request
|
|
.user_facing_hint
|
|
.unwrap_or_else(|| user_facing_hint(&target));
|
|
|
|
Ok(ResolvedReviewRequest {
|
|
target,
|
|
prompt,
|
|
user_facing_hint,
|
|
})
|
|
}
|
|
|
|
pub fn review_prompt(target: &ReviewTarget, cwd: &AbsolutePathBuf) -> anyhow::Result<String> {
|
|
match target {
|
|
ReviewTarget::UncommittedChanges => Ok(UNCOMMITTED_PROMPT.to_string()),
|
|
ReviewTarget::BaseBranch { branch } => {
|
|
if let Some(commit) = merge_base_with_head(cwd, branch)? {
|
|
Ok(render_review_prompt(
|
|
&BASE_BRANCH_PROMPT_TEMPLATE,
|
|
[
|
|
("base_branch", branch.as_str()),
|
|
("merge_base_sha", commit.as_str()),
|
|
],
|
|
))
|
|
} else {
|
|
Ok(render_review_prompt(
|
|
&BASE_BRANCH_PROMPT_BACKUP_TEMPLATE,
|
|
[("branch", branch.as_str())],
|
|
))
|
|
}
|
|
}
|
|
ReviewTarget::Commit { sha, title } => {
|
|
if let Some(title) = title {
|
|
Ok(render_review_prompt(
|
|
&COMMIT_PROMPT_WITH_TITLE_TEMPLATE,
|
|
[("sha", sha.as_str()), ("title", title.as_str())],
|
|
))
|
|
} else {
|
|
Ok(render_review_prompt(
|
|
&COMMIT_PROMPT_TEMPLATE,
|
|
[("sha", sha.as_str())],
|
|
))
|
|
}
|
|
}
|
|
ReviewTarget::Custom { instructions } => {
|
|
let prompt = instructions.trim();
|
|
if prompt.is_empty() {
|
|
anyhow::bail!("Review prompt cannot be empty");
|
|
}
|
|
Ok(prompt.to_string())
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render_review_prompt<'a, const N: usize>(
|
|
template: &Template,
|
|
variables: [(&'a str, &'a str); N],
|
|
) -> String {
|
|
template
|
|
.render(variables)
|
|
.unwrap_or_else(|err| panic!("review prompt template must render: {err}"))
|
|
}
|
|
|
|
pub fn user_facing_hint(target: &ReviewTarget) -> String {
|
|
match target {
|
|
ReviewTarget::UncommittedChanges => "current changes".to_string(),
|
|
ReviewTarget::BaseBranch { branch } => format!("changes against '{branch}'"),
|
|
ReviewTarget::Commit { sha, title } => {
|
|
let short_sha: String = sha.chars().take(7).collect();
|
|
if let Some(title) = title {
|
|
format!("commit {short_sha}: {title}")
|
|
} else {
|
|
format!("commit {short_sha}")
|
|
}
|
|
}
|
|
ReviewTarget::Custom { instructions } => instructions.trim().to_string(),
|
|
}
|
|
}
|
|
|
|
impl From<ResolvedReviewRequest> for ReviewRequest {
|
|
fn from(resolved: ResolvedReviewRequest) -> Self {
|
|
ReviewRequest {
|
|
target: resolved.target,
|
|
user_facing_hint: Some(resolved.user_facing_hint),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "review_request_tests.rs"]
|
|
mod review_request_tests;
|