use crate::pack::PackContext;
pub(crate) const BASE_INSTRUCTIONS: &str = include_str!("prompts/base_instructions.md");
pub(crate) const IDENTITY: &str = "You are Codex, an agent based on GPT-5. ";
pub(crate) fn render(ctx: &PackContext) -> String {
if ctx.strip_identity {
BASE_INSTRUCTIONS
.strip_prefix(IDENTITY)
.unwrap_or(BASE_INSTRUCTIONS)
.to_string()
} else {
BASE_INSTRUCTIONS.to_string()
}
}
pub(crate) fn environment_context(ctx: &PackContext) -> String {
use std::fmt::Write as _;
let shell = shell_name(&ctx.shell);
let mut body = format!(
"<environment_context>\n <cwd>{}</cwd>\n <shell>{}</shell>\n <current_date>{}</current_date>",
xml_escape(&ctx.cwd.to_string_lossy()),
xml_escape(&shell),
xml_escape(&ctx.date),
);
if let Some(tz) = &ctx.timezone {
let _ = write!(body, "\n <timezone>{}</timezone>", xml_escape(tz));
}
body.push_str("\n</environment_context>");
body
}
fn shell_name(shell: &str) -> String {
std::path::Path::new(shell)
.file_name()
.map_or_else(|| shell.to_string(), |n| n.to_string_lossy().into_owned())
}
fn xml_escape(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(ch),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
fn ctx(strip_identity: bool) -> PackContext {
PackContext {
cwd: PathBuf::from("/repo"),
os: "macos".into(),
shell: "/bin/zsh".into(),
date: "2026-07-24".into(),
headless: true,
is_git_repo: true,
model: Some("gpt-5.6-sol".into()),
os_version: Some("Darwin 24.6.0".into()),
timezone: Some("America/Los_Angeles".into()),
strip_identity,
}
}
#[test]
fn render_is_the_full_pinned_prompt() {
let out = render(&ctx(false));
assert_eq!(out.len(), 17766, "base_instructions byte length changed");
assert_eq!(
out.chars().count(),
17730,
"base_instructions char count changed"
);
assert!(out.starts_with("You are Codex, an agent based on GPT-5. "));
assert!(out.contains("# Personality"));
}
#[test]
fn strip_identity_removes_only_the_identity_opener() {
let out = render(&ctx(true));
assert!(out.starts_with("You and the user share one workspace"));
assert_eq!(out.len(), 17766 - IDENTITY.len());
}
#[test]
fn environment_context_renders_cwd_shell_date_tz() {
let block = environment_context(&ctx(false));
assert_eq!(
block,
"<environment_context>\n <cwd>/repo</cwd>\n <shell>zsh</shell>\n <current_date>2026-07-24</current_date>\n <timezone>America/Los_Angeles</timezone>\n</environment_context>"
);
}
#[test]
fn environment_context_omits_timezone_when_absent() {
let mut c = ctx(false);
c.timezone = None;
let block = environment_context(&c);
assert!(!block.contains("<timezone>"));
assert!(block.contains("<current_date>2026-07-24</current_date>"));
}
}