mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Show global AGENTS.md in /status (#17091)
Addresses #3793 Problem: /status only reported project-level AGENTS files, so sessions with a loaded global $CODEX_HOME/AGENTS.md still showed Agents.md as <none>. Solution: Track the global instructions file loaded during config initialization and prepend that path to the /status Agents.md summary, with coverage for AGENTS.md, AGENTS.override.md, and global-plus-project ordering.
This commit is contained in:
committed by
GitHub
Unverified
parent
4c07dd4d25
commit
dc5feb916d
@@ -128,6 +128,56 @@ fn load_config_normalizes_relative_cwd_override() -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_config_records_global_agents_path() -> std::io::Result<()> {
|
||||
let codex_home = tempdir()?;
|
||||
let global_agents_path = codex_home.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
std::fs::write(&global_agents_path, "\n global instructions \n")?;
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs().into_path_buf(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
config.user_instructions.as_deref(),
|
||||
Some("global instructions")
|
||||
);
|
||||
assert_eq!(
|
||||
config.user_instructions_path.as_deref(),
|
||||
Some(global_agents_path.as_path())
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_config_records_preferred_global_agents_override_path() -> std::io::Result<()> {
|
||||
let codex_home = tempdir()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(DEFAULT_PROJECT_DOC_FILENAME),
|
||||
"global instructions",
|
||||
)?;
|
||||
let global_agents_override_path = codex_home.path().join(LOCAL_PROJECT_DOC_FILENAME);
|
||||
std::fs::write(&global_agents_override_path, "local override instructions")?;
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs().into_path_buf(),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
config.user_instructions.as_deref(),
|
||||
Some("local override instructions")
|
||||
);
|
||||
assert_eq!(
|
||||
config.user_instructions_path.as_deref(),
|
||||
Some(global_agents_override_path.as_path())
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toml_parsing() {
|
||||
let history_with_persistence = r#"
|
||||
@@ -4457,6 +4507,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
user_instructions_path: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
cli_auth_credentials_store_mode: Default::default(),
|
||||
@@ -4603,6 +4654,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
user_instructions_path: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
cli_auth_credentials_store_mode: Default::default(),
|
||||
@@ -4747,6 +4799,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
user_instructions_path: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
cli_auth_credentials_store_mode: Default::default(),
|
||||
@@ -4877,6 +4930,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
enforce_residency: Constrained::allow_any(/*initial_value*/ None),
|
||||
user_instructions: None,
|
||||
user_instructions_path: None,
|
||||
notify: None,
|
||||
cwd: fixture.cwd(),
|
||||
cli_auth_credentials_store_mode: Default::default(),
|
||||
|
||||
@@ -245,6 +245,9 @@ pub struct Config {
|
||||
/// User-provided instructions from AGENTS.md.
|
||||
pub user_instructions: Option<String>,
|
||||
|
||||
/// Path to the global AGENTS file loaded into `user_instructions`.
|
||||
pub user_instructions_path: Option<PathBuf>,
|
||||
|
||||
/// Base instructions override.
|
||||
pub base_instructions: Option<String>,
|
||||
|
||||
@@ -1408,7 +1411,10 @@ impl Config {
|
||||
network: network_requirements,
|
||||
} = config_layer_stack.requirements().clone();
|
||||
|
||||
let user_instructions = Self::load_instructions(Some(&codex_home));
|
||||
let (user_instructions, user_instructions_path) =
|
||||
Self::load_instructions(Some(&codex_home))
|
||||
.map(|loaded| (Some(loaded.contents), Some(loaded.path)))
|
||||
.unwrap_or((None, None));
|
||||
let mut startup_warnings = Vec::new();
|
||||
|
||||
// Destructure ConfigOverrides fully to ensure all overrides are applied.
|
||||
@@ -2001,6 +2007,7 @@ impl Config {
|
||||
enforce_residency: enforce_residency.value,
|
||||
notify: cfg.notify,
|
||||
user_instructions,
|
||||
user_instructions_path,
|
||||
base_instructions,
|
||||
personality,
|
||||
developer_instructions,
|
||||
@@ -2168,7 +2175,7 @@ impl Config {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn load_instructions(codex_dir: Option<&Path>) -> Option<String> {
|
||||
fn load_instructions(codex_dir: Option<&Path>) -> Option<LoadedUserInstructions> {
|
||||
let base = codex_dir?;
|
||||
for candidate in [LOCAL_PROJECT_DOC_FILENAME, DEFAULT_PROJECT_DOC_FILENAME] {
|
||||
let mut path = base.to_path_buf();
|
||||
@@ -2176,7 +2183,10 @@ impl Config {
|
||||
if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
let trimmed = contents.trim();
|
||||
if !trimmed.is_empty() {
|
||||
return Some(trimmed.to_string());
|
||||
return Some(LoadedUserInstructions {
|
||||
contents: trimmed.to_string(),
|
||||
path,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2250,6 +2260,11 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
struct LoadedUserInstructions {
|
||||
contents: String,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
pub(crate) fn uses_deprecated_instructions_file(config_layer_stack: &ConfigLayerStack) -> bool {
|
||||
config_layer_stack
|
||||
.layers_high_to_low()
|
||||
|
||||
@@ -43,6 +43,10 @@ pub(crate) async fn discover_agents_summary(config: &Config) -> io::Result<Strin
|
||||
|
||||
pub(crate) fn compose_agents_summary(config: &Config, paths: &[AbsolutePathBuf]) -> String {
|
||||
let mut rels: Vec<String> = Vec::new();
|
||||
if let Some(path) = config.user_instructions_path.as_deref() {
|
||||
rels.push(format_directory_display(path, /*max_width*/ None));
|
||||
}
|
||||
|
||||
for p in paths {
|
||||
let file_name = p
|
||||
.file_name()
|
||||
@@ -189,7 +193,21 @@ fn title_case(s: &str) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_core::DEFAULT_PROJECT_DOC_FILENAME;
|
||||
use codex_core::LOCAL_PROJECT_DOC_FILENAME;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
async fn test_config(codex_home: &TempDir, cwd: &TempDir) -> Config {
|
||||
ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(cwd.path().to_path_buf()))
|
||||
.build()
|
||||
.await
|
||||
.expect("load config")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_type_display_name_remaps_display_labels() {
|
||||
@@ -211,4 +229,61 @@ mod tests {
|
||||
assert_eq!(plan_type_display_name(plan_type), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn discover_agents_summary_includes_global_agents_path() {
|
||||
let codex_home = TempDir::new().expect("temp codex home");
|
||||
let cwd = TempDir::new().expect("temp cwd");
|
||||
let global_agents_path = codex_home.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
fs::write(&global_agents_path, "global instructions").expect("write global agents");
|
||||
let config = test_config(&codex_home, &cwd).await;
|
||||
|
||||
assert_eq!(
|
||||
discover_agents_summary(&config).await.expect("summary"),
|
||||
format_directory_display(&global_agents_path, /*max_width*/ None)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn discover_agents_summary_names_global_agents_override() {
|
||||
let codex_home = TempDir::new().expect("temp codex home");
|
||||
let cwd = TempDir::new().expect("temp cwd");
|
||||
fs::write(
|
||||
codex_home.path().join(DEFAULT_PROJECT_DOC_FILENAME),
|
||||
"global instructions",
|
||||
)
|
||||
.expect("write global agents");
|
||||
let override_path = codex_home.path().join(LOCAL_PROJECT_DOC_FILENAME);
|
||||
fs::write(&override_path, "override instructions").expect("write global override");
|
||||
let config = test_config(&codex_home, &cwd).await;
|
||||
|
||||
assert_eq!(
|
||||
discover_agents_summary(&config).await.expect("summary"),
|
||||
format_directory_display(&override_path, /*max_width*/ None)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn discover_agents_summary_orders_global_before_project_agents() {
|
||||
let codex_home = TempDir::new().expect("temp codex home");
|
||||
let cwd = TempDir::new().expect("temp cwd");
|
||||
let global_agents_path = codex_home.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
fs::write(&global_agents_path, "global instructions").expect("write global agents");
|
||||
fs::write(
|
||||
cwd.path().join(DEFAULT_PROJECT_DOC_FILENAME),
|
||||
"project instructions",
|
||||
)
|
||||
.expect("write project agents");
|
||||
let config = test_config(&codex_home, &cwd).await;
|
||||
|
||||
let summary = discover_agents_summary(&config).await.expect("summary");
|
||||
let mut paths = summary.split(", ");
|
||||
assert_eq!(
|
||||
paths.next(),
|
||||
Some(format_directory_display(&global_agents_path, /*max_width*/ None).as_str())
|
||||
);
|
||||
let project_path = paths.next().expect("project agents path");
|
||||
assert!(project_path.ends_with(DEFAULT_PROJECT_DOC_FILENAME));
|
||||
assert_eq!(paths.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user