use std::collections::BTreeMap;
use crate::pack::PackContext;
pub const SYSTEM_PROMPT_LABEL: &str = "Grok";
const BASE_TEMPLATE: &str = include_str!("templates/prompt.md");
fn tools_by_kind() -> BTreeMap<&'static str, &'static str> {
BTreeMap::from([
("shell", "run_terminal_cmd"),
("read", "read_file"),
("edit", "search_replace"),
("grep", "grep"),
("glob", "list_dir"),
])
}
fn make_env() -> minijinja::Environment<'static> {
use minijinja::syntax::SyntaxConfig;
let syntax = SyntaxConfig::builder()
.block_delimiters("${%", "%}")
.variable_delimiters("${{", "}}")
.comment_delimiters("${#", "#}")
.build()
.unwrap_or_else(|e| panic!("custom syntax config is valid: {e}"));
let mut env = minijinja::Environment::new();
env.set_syntax(syntax);
env
}
const IDENTITY_SENTENCE: &str = "You are Grok released by xAI. ";
#[must_use]
pub fn render_base_prompt(ctx: &PackContext) -> String {
let render_ctx = minijinja::context! {
tools => minijinja::context! { by_kind => tools_by_kind() },
system_prompt_label => SYSTEM_PROMPT_LABEL,
is_non_interactive => ctx.headless,
os_name => ctx.os,
shell_path => ctx.shell,
working_directory => ctx.cwd.to_string_lossy(),
current_date => ctx.date,
memory_enabled => false,
};
let rendered = make_env()
.render_str(BASE_TEMPLATE, render_ctx)
.unwrap_or_else(|e| panic!("grok prompt template failed to render: {e}"));
debug_assert!(
!rendered.contains("${{") && !rendered.contains("${%"),
"unresolved template tokens in the rendered grok prompt"
);
if ctx.strip_identity {
strip_identity(&rendered)
} else {
rendered
}
}
fn strip_identity(rendered: &str) -> String {
let mut out = rendered
.strip_prefix(IDENTITY_SENTENCE)
.map_or_else(|| rendered.to_string(), str::to_string);
if let (Some(start), Some(end)) = (out.find("\n\n<user_guide>"), out.find("</user_guide>"))
&& start < end
{
out.replace_range(start..end + "</user_guide>".len(), "");
}
out
}
#[must_use]
pub fn user_info_block(ctx: &PackContext) -> String {
let os = &ctx.os;
let shell = &ctx.shell;
let cwd = ctx.cwd.to_string_lossy();
let today = &ctx.date;
format!(
r"<user_info>
OS Version: {os}
Shell: {shell}
Workspace Path: {cwd}
Today's date: {today}
Note: Prefer using relative paths over absolute paths as tool call args when possible.
</user_info>",
)
}
#[must_use]
pub fn user_query(user_message: &str) -> String {
format!(
r"<user_query>
{user_message}
</user_query>"
)
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
fn ctx(headless: bool) -> PackContext {
PackContext {
cwd: PathBuf::from("/tmp/test"),
os: "macos".into(),
shell: "/bin/zsh".into(),
date: "2025-01-15".into(),
headless,
is_git_repo: false,
model: None,
os_version: None,
timezone: None,
strip_identity: false,
}
}
#[test]
fn template_copy_is_pinned() {
assert_eq!(BASE_TEMPLATE.len(), 4638, "template byte length changed");
assert!(
BASE_TEMPLATE
.starts_with("You are ${{ system_prompt_label }} released by xAI. You are ")
);
assert!(!BASE_TEMPLATE.contains('\r'), "no CRLF in the copy");
}
#[test]
fn rendered_prompt_matches_snapshots() {
for (headless, name) in [
(true, "prompt_headless.txt"),
(false, "prompt_interactive.txt"),
] {
let rendered = render_base_prompt(&ctx(headless));
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_string());
let path = format!("{manifest_dir}/src/grok/snapshots/{name}");
if std::env::var("UPDATE_SNAPSHOTS").is_ok() {
std::fs::write(&path, &rendered)
.unwrap_or_else(|e| panic!("write snapshot {path}: {e}"));
continue;
}
let expected = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("read snapshot {path} (run UPDATE_SNAPSHOTS=1): {e}"));
assert_eq!(rendered, expected, "snapshot drift: {name}");
}
}
#[test]
fn headless_renders_the_autonomous_identity() {
let prompt = render_base_prompt(&ctx(true));
assert!(prompt.starts_with(
"You are Grok released by xAI. You are an autonomous agent that \
completes software engineering tasks."
));
assert!(!prompt.contains("interactive CLI tool"));
assert!(
!prompt.contains("<user_guide>"),
"user_guide is interactive-only"
);
}
#[test]
fn interactive_renders_the_cli_identity_and_user_guide() {
let prompt = render_base_prompt(&ctx(false));
assert!(prompt.starts_with(
"You are Grok released by xAI. You are an interactive CLI tool that \
helps users with software engineering tasks."
));
assert!(prompt.contains("<user_guide>"));
assert!(!prompt.contains("autonomous agent"));
}
#[test]
fn tool_names_resolve_and_no_tokens_leak() {
let prompt = render_base_prompt(&ctx(true));
assert!(prompt.contains("`read_file`"), "by_kind.read resolves");
assert!(prompt.contains("`search_replace`"), "by_kind.edit resolves");
assert!(!prompt.contains("${{"), "no unresolved variables");
assert!(!prompt.contains("${%"), "no unresolved blocks");
assert!(!prompt.contains("<background_tasks>"));
}
#[test]
fn rendered_prompt_within_grok_size_budget() {
assert!(render_base_prompt(&ctx(true)).len() < 16384);
}
#[test]
fn strip_identity_removes_the_opening_sentence_headless() {
let mut c = ctx(true);
c.strip_identity = true;
let prompt = render_base_prompt(&c);
assert!(
prompt.starts_with("You are an autonomous agent"),
"identity sentence removed, opener intact"
);
assert!(!prompt.contains("Grok"), "no identity leakage");
assert!(!prompt.contains("xAI"), "no vendor leakage");
}
#[test]
fn strip_identity_also_drops_the_user_guide_when_interactive() {
let mut c = ctx(false);
c.strip_identity = true;
let prompt = render_base_prompt(&c);
assert!(prompt.starts_with("You are an interactive CLI tool"));
assert!(!prompt.contains("<user_guide>"));
assert!(!prompt.contains("Grok"), "user_guide named Grok Build");
assert!(!prompt.contains("xAI"));
}
#[test]
fn identity_sentence_pin_matches_the_render() {
let faithful = render_base_prompt(&ctx(true));
assert!(faithful.starts_with(IDENTITY_SENTENCE));
}
#[test]
fn user_info_block_is_byte_exact() {
let expected = "<user_info>\n\
OS Version: macos\n\
Shell: /bin/zsh\n\
Workspace Path: /tmp/test\n\
Today's date: 2025-01-15\n\
Note: Prefer using relative paths over absolute paths as tool call args when possible.\n\
</user_info>";
assert_eq!(user_info_block(&ctx(true)), expected);
}
#[test]
fn user_query_wraps_verbatim() {
assert_eq!(
user_query("fix the bug"),
"<user_query>\nfix the bug\n</user_query>"
);
}
}