mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Make AGENTS.md discovery FS-aware (#15826)
## Summary - make AGENTS.md discovery and loading fully FS-aware and remove the non-FS discover helper - migrate remote-aware codex-core tests to use TestEnv workspace setup instead of syncing a local workspace copy - add AGENTS.md corner-case coverage, including directory fallbacks and remote-aware integration coverage ## Testing - cargo test -p codex-core project_doc -- --nocapture - cargo test -p codex-core hierarchical_agents -- --nocapture - cargo test -p codex-core agents_md -- --nocapture - cargo test -p codex-tui status -- --nocapture - cargo test -p codex-tui-app-server status -- --nocapture - just fix - just fmt - just bazel-lock-update - just bazel-lock-check - just argument-comment-lint - remote Linux executor tests in progress via scripts/test-remote-env.sh
This commit is contained in:
@@ -7303,6 +7303,8 @@ impl ChatWidget {
|
||||
.values()
|
||||
.cloned()
|
||||
.collect();
|
||||
let config = self.config.clone();
|
||||
let frame_requester = self.frame_requester.clone();
|
||||
let (cell, handle) = crate::status::new_status_output_with_rate_limits_handle(
|
||||
&self.config,
|
||||
self.status_account_display.as_ref(),
|
||||
@@ -7317,8 +7319,21 @@ impl ChatWidget {
|
||||
self.model_display_name(),
|
||||
collaboration_mode,
|
||||
reasoning_effort_override,
|
||||
"<none>".to_string(),
|
||||
refreshing_rate_limits,
|
||||
);
|
||||
let agents_summary_handle = handle.clone();
|
||||
tokio::spawn(async move {
|
||||
let agents_summary = match crate::status::discover_agents_summary(&config).await {
|
||||
Ok(summary) => summary,
|
||||
Err(err) => {
|
||||
tracing::warn!(error = %err, "failed to discover project docs for /status");
|
||||
"<none>".to_string()
|
||||
}
|
||||
};
|
||||
agents_summary_handle.finish_agents_summary_discovery(agents_summary);
|
||||
frame_requester.schedule_frame();
|
||||
});
|
||||
if let Some(request_id) = request_id {
|
||||
self.refreshing_status_outputs.push((request_id, handle));
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ use super::format::line_display_width;
|
||||
use super::format::push_label;
|
||||
use super::format::truncate_line_to_width;
|
||||
use super::helpers::compose_account_display;
|
||||
use super::helpers::compose_agents_summary;
|
||||
use super::helpers::compose_model_display;
|
||||
use super::helpers::format_directory_display;
|
||||
use super::helpers::format_tokens_compact;
|
||||
@@ -68,10 +67,20 @@ struct StatusRateLimitState {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct StatusHistoryHandle {
|
||||
agents_summary: Arc<RwLock<String>>,
|
||||
rate_limit_state: Arc<RwLock<StatusRateLimitState>>,
|
||||
}
|
||||
|
||||
impl StatusHistoryHandle {
|
||||
pub(crate) fn finish_agents_summary_discovery(&self, agents_summary: String) {
|
||||
#[expect(clippy::expect_used)]
|
||||
let mut current = self
|
||||
.agents_summary
|
||||
.write()
|
||||
.expect("status history agents summary state poisoned");
|
||||
*current = agents_summary;
|
||||
}
|
||||
|
||||
pub(crate) fn finish_rate_limit_refresh(
|
||||
&self,
|
||||
rate_limits: &[RateLimitSnapshotDisplay],
|
||||
@@ -98,7 +107,7 @@ struct StatusHistoryCell {
|
||||
model_details: Vec<String>,
|
||||
directory: PathBuf,
|
||||
permissions: String,
|
||||
agents_summary: String,
|
||||
agents_summary: Arc<RwLock<String>>,
|
||||
collaboration_mode: Option<String>,
|
||||
model_provider: Option<String>,
|
||||
account: Option<StatusAccountDisplay>,
|
||||
@@ -177,6 +186,7 @@ pub(crate) fn new_status_output_with_rate_limits(
|
||||
model_name,
|
||||
collaboration_mode,
|
||||
reasoning_effort_override,
|
||||
"<none>".to_string(),
|
||||
refreshing_rate_limits,
|
||||
)
|
||||
.0
|
||||
@@ -197,6 +207,7 @@ pub(crate) fn new_status_output_with_rate_limits_handle(
|
||||
model_name: &str,
|
||||
collaboration_mode: Option<&str>,
|
||||
reasoning_effort_override: Option<Option<ReasoningEffort>>,
|
||||
agents_summary: String,
|
||||
refreshing_rate_limits: bool,
|
||||
) -> (CompositeHistoryCell, StatusHistoryHandle) {
|
||||
let command = PlainHistoryCell::new(vec!["/status".magenta().into()]);
|
||||
@@ -214,6 +225,7 @@ pub(crate) fn new_status_output_with_rate_limits_handle(
|
||||
model_name,
|
||||
collaboration_mode,
|
||||
reasoning_effort_override,
|
||||
agents_summary,
|
||||
refreshing_rate_limits,
|
||||
);
|
||||
|
||||
@@ -239,6 +251,7 @@ impl StatusHistoryCell {
|
||||
model_name: &str,
|
||||
collaboration_mode: Option<&str>,
|
||||
reasoning_effort_override: Option<Option<ReasoningEffort>>,
|
||||
agents_summary: String,
|
||||
refreshing_rate_limits: bool,
|
||||
) -> (Self, StatusHistoryHandle) {
|
||||
let mut config_entries = vec![
|
||||
@@ -302,7 +315,6 @@ impl StatusHistoryCell {
|
||||
} else {
|
||||
format!("Custom ({sandbox}, {approval})")
|
||||
};
|
||||
let agents_summary = compose_agents_summary(config);
|
||||
let model_provider = format_model_provider(config);
|
||||
let account = compose_account_display(account_display);
|
||||
let session_id = session_id.as_ref().map(std::string::ToString::to_string);
|
||||
@@ -333,6 +345,7 @@ impl StatusHistoryCell {
|
||||
rate_limits,
|
||||
refreshing_rate_limits,
|
||||
}));
|
||||
let agents_summary = Arc::new(RwLock::new(agents_summary));
|
||||
|
||||
(
|
||||
Self {
|
||||
@@ -340,7 +353,6 @@ impl StatusHistoryCell {
|
||||
model_details,
|
||||
directory: config.cwd.to_path_buf(),
|
||||
permissions,
|
||||
agents_summary,
|
||||
collaboration_mode: collaboration_mode.map(ToString::to_string),
|
||||
model_provider,
|
||||
account,
|
||||
@@ -348,9 +360,13 @@ impl StatusHistoryCell {
|
||||
session_id,
|
||||
forked_from,
|
||||
token_usage,
|
||||
agents_summary: agents_summary.clone(),
|
||||
rate_limit_state: rate_limit_state.clone(),
|
||||
},
|
||||
StatusHistoryHandle { rate_limit_state },
|
||||
StatusHistoryHandle {
|
||||
agents_summary,
|
||||
rate_limit_state,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -564,6 +580,12 @@ impl HistoryCell for StatusHistoryCell {
|
||||
.rate_limit_state
|
||||
.read()
|
||||
.expect("status history rate-limit state poisoned");
|
||||
#[expect(clippy::expect_used)]
|
||||
let agents_summary = self
|
||||
.agents_summary
|
||||
.read()
|
||||
.expect("status history agents summary state poisoned")
|
||||
.clone();
|
||||
|
||||
if self.model_provider.is_some() {
|
||||
push_label(&mut labels, &mut seen, "Model provider");
|
||||
@@ -625,7 +647,7 @@ impl HistoryCell for StatusHistoryCell {
|
||||
}
|
||||
lines.push(formatter.line("Directory", vec![Span::from(directory_value)]));
|
||||
lines.push(formatter.line("Permissions", vec![Span::from(self.permissions.clone())]));
|
||||
lines.push(formatter.line("Agents.md", vec![Span::from(self.agents_summary.clone())]));
|
||||
lines.push(formatter.line("Agents.md", vec![Span::from(agents_summary)]));
|
||||
|
||||
if let Some(account_value) = account_value {
|
||||
lines.push(formatter.line("Account", vec![Span::from(account_value)]));
|
||||
|
||||
@@ -5,7 +5,10 @@ use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::project_doc::discover_project_doc_paths;
|
||||
use codex_exec_server::LOCAL_FS;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
@@ -33,51 +36,52 @@ pub(crate) fn compose_model_display(
|
||||
(model_name.to_string(), details)
|
||||
}
|
||||
|
||||
pub(crate) fn compose_agents_summary(config: &Config) -> String {
|
||||
match discover_project_doc_paths(config) {
|
||||
Ok(paths) => {
|
||||
let mut rels: Vec<String> = Vec::new();
|
||||
for p in paths {
|
||||
let file_name = p
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let display = if let Some(parent) = p.parent() {
|
||||
if parent == config.cwd.as_path() {
|
||||
file_name.clone()
|
||||
} else {
|
||||
let mut cur = config.cwd.as_path();
|
||||
let mut ups = 0usize;
|
||||
let mut reached = false;
|
||||
while let Some(c) = cur.parent() {
|
||||
if cur == parent {
|
||||
reached = true;
|
||||
break;
|
||||
}
|
||||
cur = c;
|
||||
ups += 1;
|
||||
}
|
||||
if reached {
|
||||
let up = format!("..{}", std::path::MAIN_SEPARATOR);
|
||||
format!("{}{}", up.repeat(ups), file_name)
|
||||
} else if let Ok(stripped) = p.strip_prefix(&config.cwd) {
|
||||
normalize_agents_display_path(stripped)
|
||||
} else {
|
||||
normalize_agents_display_path(&p)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
normalize_agents_display_path(&p)
|
||||
};
|
||||
rels.push(display);
|
||||
}
|
||||
if rels.is_empty() {
|
||||
"<none>".to_string()
|
||||
pub(crate) async fn discover_agents_summary(config: &Config) -> io::Result<String> {
|
||||
let paths = discover_project_doc_paths(config, LOCAL_FS.as_ref()).await?;
|
||||
Ok(compose_agents_summary(config, &paths))
|
||||
}
|
||||
|
||||
pub(crate) fn compose_agents_summary(config: &Config, paths: &[AbsolutePathBuf]) -> String {
|
||||
let mut rels: Vec<String> = Vec::new();
|
||||
for p in paths {
|
||||
let file_name = p
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let display = if let Some(parent) = p.parent() {
|
||||
if parent.as_path() == config.cwd.as_path() {
|
||||
file_name.clone()
|
||||
} else {
|
||||
rels.join(", ")
|
||||
let mut cur = config.cwd.as_path();
|
||||
let mut ups = 0usize;
|
||||
let mut reached = false;
|
||||
while let Some(c) = cur.parent() {
|
||||
if cur == parent.as_path() {
|
||||
reached = true;
|
||||
break;
|
||||
}
|
||||
cur = c;
|
||||
ups += 1;
|
||||
}
|
||||
if reached {
|
||||
let up = format!("..{}", std::path::MAIN_SEPARATOR);
|
||||
format!("{}{}", up.repeat(ups), file_name)
|
||||
} else if let Ok(stripped) = p.strip_prefix(&config.cwd) {
|
||||
normalize_agents_display_path(stripped)
|
||||
} else {
|
||||
normalize_agents_display_path(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => "<none>".to_string(),
|
||||
} else {
|
||||
normalize_agents_display_path(p)
|
||||
};
|
||||
rels.push(display);
|
||||
}
|
||||
|
||||
if rels.is_empty() {
|
||||
"<none>".to_string()
|
||||
} else {
|
||||
rels.join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(crate) use card::new_status_output;
|
||||
#[cfg(test)]
|
||||
pub(crate) use card::new_status_output_with_rate_limits;
|
||||
pub(crate) use card::new_status_output_with_rate_limits_handle;
|
||||
pub(crate) use helpers::discover_agents_summary;
|
||||
pub(crate) use helpers::format_directory_display;
|
||||
pub(crate) use helpers::format_tokens_compact;
|
||||
pub(crate) use helpers::plan_type_display_name;
|
||||
|
||||
Reference in New Issue
Block a user