use std::path::Path;
const INSTRUCTION_FILES: [&str; 2] = ["CLAUDE.md", "AGENTS.md"];
pub const MAX_INSTRUCTIONS_BYTES: usize = 64_000;
const MAX_NESTED_INSTRUCTIONS_BYTES: usize = 12_000;
const MAX_NESTED_FILE_BYTES: usize = 6_000;
const MAX_NESTED_FILES: usize = 8;
const MIN_USEFUL_NESTED_BYTES: usize = 400;
pub const MAX_KNOWLEDGE_BYTES: usize = 8_000;
const MAX_KNOWLEDGE_ENTRIES: usize = 40;
pub fn agent_instructions(worktree: &Path) -> Option<(String, String)> {
for name in INSTRUCTION_FILES {
let path = worktree.join(name);
let Ok(raw) = std::fs::read_to_string(&path) else {
continue;
};
if raw.trim().is_empty() {
continue;
}
return Some((
name.to_string(),
truncate_note(&raw, MAX_INSTRUCTIONS_BYTES),
));
}
None
}
pub fn nested_instructions(worktree: &Path) -> Option<String> {
let paths = tracked_instruction_files(worktree)?;
if paths.is_empty() {
return None;
}
let mut out = String::new();
let mut used = 0usize;
let mut deferred: Vec<String> = Vec::new();
for (i, rel) in paths.iter().enumerate() {
let dir = rel.rsplit_once('/').map(|(d, _)| d).unwrap_or(".");
let over_budget = i >= MAX_NESTED_FILES || used >= MAX_NESTED_INSTRUCTIONS_BYTES;
let raw = if over_budget {
String::new()
} else {
std::fs::read_to_string(worktree.join(rel)).unwrap_or_default()
};
if over_budget || raw.trim().is_empty() {
if over_budget {
deferred.push(rel.clone());
}
continue;
}
let remaining = MAX_NESTED_INSTRUCTIONS_BYTES.saturating_sub(used);
if remaining < MIN_USEFUL_NESTED_BYTES && raw.trim().len() > remaining {
deferred.push(rel.clone());
continue;
}
let body = truncate_note(raw.trim(), MAX_NESTED_FILE_BYTES.min(remaining));
used += body.len();
out.push_str(&format!(
"--- {rel} — applies to everything under `{dir}/` ---\n{body}\n\n"
));
}
if !deferred.is_empty() {
out.push_str(
"Not shown, over budget. Read the file before editing anything under its \
directory:\n",
);
for rel in &deferred {
out.push_str(&format!("- {rel}\n"));
}
}
let trimmed = out.trim();
(!trimmed.is_empty()).then(|| trimmed.to_string())
}
fn tracked_instruction_files(worktree: &Path) -> Option<Vec<String>> {
let out = std::process::Command::new("git")
.arg("-C")
.arg(worktree)
.args(["ls-files", "-z", "--", "*CLAUDE.md", "*AGENTS.md"])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let mut paths: Vec<String> = String::from_utf8_lossy(&out.stdout)
.split('\0')
.filter(|p| !p.is_empty())
.filter(|p| p.contains('/'))
.filter(|p| {
let name = p.rsplit('/').next().unwrap_or(p);
INSTRUCTION_FILES.contains(&name)
})
.map(str::to_string)
.collect();
paths.sort_by_key(|p| (p.matches('/').count(), p.clone()));
Some(paths)
}
pub fn dot_car_knowledge(worktree: &Path) -> Option<String> {
let car_dir = car_memgine::project::discover_project(worktree)?;
let project = car_memgine::project::load_project(&car_dir).ok()?;
let mut out = String::new();
if let Some(identity) = project.identity.as_deref().map(str::trim) {
if !identity.is_empty() {
out.push_str(identity);
out.push_str("\n\n");
}
}
if !project.knowledge.is_empty() {
out.push_str("Recorded project knowledge:\n");
for entry in project.knowledge.iter().take(MAX_KNOWLEDGE_ENTRIES) {
let kind = if entry.entry_type.is_empty() {
"note"
} else {
&entry.entry_type
};
out.push_str(&format!("- [{kind}] {}", entry.fact.trim()));
let recommendation = entry.recommendation.trim();
if !recommendation.is_empty() {
out.push_str(&format!(" — {recommendation}"));
}
out.push('\n');
}
}
let trimmed = out.trim();
(!trimmed.is_empty()).then(|| truncate_note(trimmed, MAX_KNOWLEDGE_BYTES))
}
pub fn project_context(worktree: &Path) -> Option<String> {
let instructions = agent_instructions(worktree);
let nested = nested_instructions(worktree);
let knowledge = dot_car_knowledge(worktree);
let skills = instructions
.is_some()
.then(|| available_skills(worktree))
.flatten();
if instructions.is_none() && nested.is_none() && knowledge.is_none() {
return None;
}
let mut out = String::new();
if let Some((name, body)) = instructions {
out.push_str(&format!(
"PROJECT INSTRUCTIONS (from {name}, written by this repository's maintainers).\n\
These are review-time rules. Your outcome contract does NOT check them, so a \
diff can pass every check and still be rejected for breaking one. Follow them \
as constraints on HOW you implement, and never weaken or edit a contract check \
to satisfy one — the contract decides whether the work is done, these decide \
whether it is acceptable. Where a rule points at another document you have not \
been given, say so in your summary rather than guessing at its contents.\n\n\
{body}\n"
));
}
if let Some(nested) = nested {
out.push_str(&format!(
"\nDIRECTORY-SCOPED INSTRUCTIONS.\n\
Each block below governs one subtree and carries the same weight as the \
instructions above while you are working inside it. Where a scoped rule is \
stricter than a root one, the scoped rule wins for that subtree.\n\n{nested}\n"
));
}
if let Some(skills) = skills {
out.push_str(&format!("\nPROJECT SKILLS.\n{skills}\n"));
}
if let Some(knowledge) = knowledge {
if !out.is_empty() {
out.push('\n');
}
out.push_str(&format!(
"PROJECT KNOWLEDGE (from .car/, recorded by the team).\n\n{knowledge}\n"
));
}
Some(out)
}
pub fn available_skills(worktree: &Path) -> Option<String> {
let skills_dir = worktree.join(".claude").join("skills");
let mut entries: Vec<(String, String)> = std::fs::read_dir(&skills_dir)
.ok()?
.flatten()
.filter_map(|entry| {
let manifest = entry.path().join("SKILL.md");
let raw = std::fs::read_to_string(&manifest).ok()?;
let (name, description) = parse_frontmatter(&raw)?;
let rel = format!(
".claude/skills/{}/SKILL.md",
entry.file_name().to_string_lossy()
);
Some((rel, format!("**{name}** — {description}")))
})
.collect();
if entries.is_empty() {
return None;
}
entries.sort();
let mut out = String::from(
"The instructions above delegate to these skill documents. They are files in this \
worktree: when your work touches an area one of them covers, read it with \
`read_file` BEFORE editing, rather than guessing at what it says.\n\n",
);
for (path, summary) in entries {
out.push_str(&format!("- `{path}` — {summary}\n"));
}
Some(truncate_note(out.trim(), MAX_KNOWLEDGE_BYTES))
}
fn parse_frontmatter(raw: &str) -> Option<(String, String)> {
let body = raw.strip_prefix("---")?;
let end = body.find("\n---")?;
let block = &body[..end];
let mut name = None;
let mut description: Option<String> = None;
let mut in_description = false;
for line in block.lines() {
if let Some(rest) = line.strip_prefix("name:") {
name = Some(rest.trim().to_string());
in_description = false;
} else if let Some(rest) = line.strip_prefix("description:") {
let first = rest.trim().trim_start_matches(['>', '|', '-']).trim();
description = Some(first.to_string());
in_description = true;
} else if in_description {
let indented = line.starts_with(' ') || line.starts_with('\t');
if indented && !line.trim().is_empty() {
let existing = description.get_or_insert_with(String::new);
if !existing.is_empty() {
existing.push(' ');
}
existing.push_str(line.trim());
} else if !line.trim().is_empty() {
in_description = false;
}
}
}
let name = name.filter(|n| !n.is_empty())?;
let description = description
.map(|d| first_sentence(&d))
.filter(|d| !d.is_empty())?;
Some((name, description))
}
fn first_sentence(text: &str) -> String {
match text.find(". ") {
Some(i) => text[..=i].trim().to_string(),
None => text.trim().to_string(),
}
}
fn truncate_note(text: &str, max: usize) -> String {
if text.len() <= max {
return text.to_string();
}
let mut end = max;
while end > 0 && !text.is_char_boundary(end) {
end -= 1;
}
format!(
"{}\n\n[truncated: {} of {} bytes shown]",
&text[..end],
end,
text.len()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_claude_md_and_prefers_it_over_agents_md() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), "no feature flags, ever").unwrap();
std::fs::write(dir.path().join("AGENTS.md"), "something else").unwrap();
let (name, body) = agent_instructions(dir.path()).expect("instructions found");
assert_eq!(name, "CLAUDE.md");
assert!(body.contains("no feature flags"));
}
#[test]
fn falls_back_to_agents_md() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("AGENTS.md"), "house rules").unwrap();
let (name, _) = agent_instructions(dir.path()).expect("instructions found");
assert_eq!(name, "AGENTS.md");
}
#[test]
fn an_empty_instruction_file_is_not_instructions() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), " \n\n").unwrap();
assert!(agent_instructions(dir.path()).is_none());
}
#[test]
fn a_repo_with_nothing_yields_no_block() {
let dir = tempfile::tempdir().unwrap();
assert!(project_context(dir.path()).is_none());
}
#[test]
fn the_block_says_the_contract_does_not_check_these_rules() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), "rule one").unwrap();
let block = project_context(dir.path()).expect("block");
assert!(block.contains("does NOT check them"));
assert!(block.contains("never weaken or edit a contract check"));
assert!(block.contains("rule one"));
}
#[test]
fn truncation_is_announced_not_silent() {
let dir = tempfile::tempdir().unwrap();
let huge = "x".repeat(MAX_INSTRUCTIONS_BYTES + 500);
std::fs::write(dir.path().join("CLAUDE.md"), &huge).unwrap();
let (_, body) = agent_instructions(dir.path()).expect("instructions");
assert!(
body.contains("[truncated:"),
"a silent cut hides missing rules"
);
assert!(body.len() < huge.len() + 200);
}
#[test]
fn truncation_lands_on_a_char_boundary() {
let text = "é".repeat(100);
let cut = truncate_note(&text, 51);
assert!(cut.contains("[truncated:"));
}
fn write_skill(root: &std::path::Path, dir: &str, frontmatter: &str) {
let d = root.join(".claude").join("skills").join(dir);
std::fs::create_dir_all(&d).unwrap();
std::fs::write(d.join("SKILL.md"), frontmatter).unwrap();
}
#[test]
fn skills_are_indexed_by_name_and_first_sentence() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), "see the skills").unwrap();
write_skill(
dir.path(),
"car-bindings-api",
"---\nname: car-bindings-api\ndescription: >-\n The complete CAR bindings API\n surface. Use it whenever work touches the FFI boundary.\n---\nbody text here\n",
);
let block = project_context(dir.path()).expect("block");
assert!(block.contains("car-bindings-api"));
assert!(block.contains("The complete CAR bindings API surface."));
assert!(block.contains(".claude/skills/car-bindings-api/SKILL.md"));
assert!(block.contains("read_file"));
assert!(!block.contains("body text here"));
}
#[test]
fn a_folded_description_is_joined_not_truncated_at_the_newline() {
let (name, desc) = parse_frontmatter(
"---\nname: thing\ndescription: >-\n first part\n second part. Rest.\n---\n",
)
.expect("parsed");
assert_eq!(name, "thing");
assert_eq!(desc, "first part second part.");
}
#[test]
fn a_skill_without_usable_frontmatter_is_skipped_not_guessed() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), "rules").unwrap();
write_skill(dir.path(), "broken", "no frontmatter at all\n");
write_skill(
dir.path(),
"good",
"---\nname: good\ndescription: Does a thing.\n---\n",
);
let block = project_context(dir.path()).expect("block");
assert!(block.contains("good"));
assert!(!block.contains("broken"));
}
#[test]
fn skills_are_not_volunteered_without_instructions_that_delegate() {
let dir = tempfile::tempdir().unwrap();
write_skill(
dir.path(),
"lonely",
"---\nname: lonely\ndescription: Nobody points here.\n---\n",
);
assert!(project_context(dir.path()).is_none());
}
#[test]
fn dot_car_knowledge_is_rendered_with_recommendations() {
let dir = tempfile::tempdir().unwrap();
let car = dir.path().join(".car");
std::fs::create_dir_all(car.join("knowledge")).unwrap();
std::fs::write(car.join("identity.md"), "The CAR runtime.").unwrap();
std::fs::write(
car.join("knowledge").join("gotchas.jsonl"),
r#"{"id":"g1","type":"gotcha","fact":"cargo config follows cwd","recommendation":"run from car-rs"}"#,
)
.unwrap();
let rendered = dot_car_knowledge(dir.path()).expect("knowledge loaded");
assert!(rendered.contains("The CAR runtime."));
assert!(rendered.contains("cargo config follows cwd"));
assert!(rendered.contains("run from car-rs"));
assert!(rendered.contains("[gotcha]"));
}
#[test]
fn discovery_walks_up_from_a_nested_worktree() {
let dir = tempfile::tempdir().unwrap();
std::fs::create_dir_all(dir.path().join(".car")).unwrap();
std::fs::write(dir.path().join(".car").join("identity.md"), "root project").unwrap();
let nested = dir.path().join("crates").join("thing");
std::fs::create_dir_all(&nested).unwrap();
let rendered = dot_car_knowledge(&nested).expect("found by walking up");
assert!(rendered.contains("root project"));
}
fn repo_with(files: &[(&str, &str)]) -> tempfile::TempDir {
let dir = tempfile::tempdir().unwrap();
let git = |args: &[&str]| {
let ok = std::process::Command::new("git")
.arg("-C")
.arg(dir.path())
.args(args)
.output()
.unwrap()
.status
.success();
assert!(ok, "git {args:?} failed");
};
git(&["init", "-q"]);
git(&["config", "user.email", "t@example.com"]);
git(&["config", "user.name", "t"]);
for (rel, body) in files {
let path = dir.path().join(rel);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, body).unwrap();
}
git(&["add", "-A"]);
git(&["commit", "-qm", "seed"]);
dir
}
#[test]
fn a_nested_instruction_file_is_inlined_with_the_directory_it_governs() {
let dir = repo_with(&[
("CLAUDE.md", "root rules"),
("crates/napi/CLAUDE.md", "do not reintroduce the five bugs"),
]);
let nested = nested_instructions(dir.path()).expect("a nested file is found");
assert!(
nested.contains("do not reintroduce the five bugs"),
"the nested rule must be inlined, not merely pointed at: {nested}"
);
assert!(
nested.contains("crates/napi/CLAUDE.md") && nested.contains("`crates/napi/`"),
"a scoped rule shown without its scope reads as a global one: {nested}"
);
}
#[test]
fn the_root_file_is_not_repeated_in_the_nested_block() {
let dir = repo_with(&[("CLAUDE.md", "root rules"), ("sub/CLAUDE.md", "sub rules")]);
let nested = nested_instructions(dir.path()).unwrap();
assert!(
!nested.contains("root rules"),
"agent_instructions already loads the root file in full: {nested}"
);
}
#[test]
fn an_untracked_instruction_file_is_ignored() {
let dir = repo_with(&[("CLAUDE.md", "root rules"), ("sub/CLAUDE.md", "tracked")]);
std::fs::create_dir_all(dir.path().join("target/scratch")).unwrap();
std::fs::write(
dir.path().join("target/scratch/CLAUDE.md"),
"not maintainer intent",
)
.unwrap();
let nested = nested_instructions(dir.path()).unwrap();
assert!(nested.contains("tracked"));
assert!(
!nested.contains("not maintainer intent"),
"untracked text must not reach the system prompt — a session could \
otherwise write its own rules mid-run: {nested}"
);
}
#[test]
fn a_worktree_that_is_not_a_git_checkout_yields_nothing_rather_than_failing() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("CLAUDE.md"), "rules").unwrap();
assert!(nested_instructions(dir.path()).is_none());
assert!(project_context(dir.path()).unwrap().contains("rules"));
}
#[test]
fn past_the_file_budget_the_rest_become_readable_pointers() {
let mut files: Vec<(String, String)> = vec![("CLAUDE.md".into(), "root".into())];
for i in 0..(MAX_NESTED_FILES + 3) {
files.push((format!("d{i:02}/CLAUDE.md"), format!("rule {i}")));
}
let refs: Vec<(&str, &str)> = files
.iter()
.map(|(a, b)| (a.as_str(), b.as_str()))
.collect();
let dir = repo_with(&refs);
let nested = nested_instructions(dir.path()).unwrap();
assert!(
nested.contains("Not shown, over budget"),
"a repo past the budget must be TOLD it is seeing pointers: {nested}"
);
assert!(
nested.contains("rule 0"),
"the first files are still inlined"
);
assert!(
nested.contains(&format!("d{:02}/CLAUDE.md", MAX_NESTED_FILES + 2)),
"an over-budget file is still named so the model can read it"
);
}
#[test]
fn nested_instructions_reach_the_prompt_block_with_their_precedence_stated() {
let dir = repo_with(&[
("CLAUDE.md", "root rules"),
("crates/napi/CLAUDE.md", "napi gotchas"),
]);
let block = project_context(dir.path()).expect("a block");
assert!(block.contains("DIRECTORY-SCOPED INSTRUCTIONS"));
assert!(block.contains("napi gotchas"));
assert!(
block.contains("the scoped rule wins for that subtree"),
"precedence between root and scoped rules must be stated, not guessed: {block}"
);
}
#[test]
fn car_s_own_repo_yields_both_its_hard_rules_and_its_napi_gotchas() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(3)
.unwrap();
if !root.join("car-rs/crates/car-ffi-napi/CLAUDE.md").exists() {
return;
}
let block = project_context(root).expect("CAR has instructions");
assert!(
block.contains("No cargo feature flags"),
"the hard rules must survive the root budget"
);
assert!(block.contains("Keep all FFI bindings in sync"));
assert!(
!block.contains("[truncated:"),
"CAR's own instructions must not be truncated at all"
);
assert!(
block.contains("Do not reintroduce them"),
"car-ffi-napi/CLAUDE.md must reach the prompt"
);
assert!(block.contains("car-rs/crates/car-ffi-napi/CLAUDE.md"));
}
#[test]
fn a_file_that_would_only_fit_as_a_fragment_is_deferred_instead() {
let big = "r".repeat(MAX_NESTED_FILE_BYTES);
let mut files: Vec<(String, String)> = vec![("CLAUDE.md".into(), "root".into())];
for i in 0..2 {
files.push((format!("d{i}/CLAUDE.md"), big.clone()));
}
files.push(("zz/CLAUDE.md".into(), "z".repeat(5_000)));
let refs: Vec<(&str, &str)> = files
.iter()
.map(|(a, b)| (a.as_str(), b.as_str()))
.collect();
let dir = repo_with(&refs);
let nested = nested_instructions(dir.path()).unwrap();
assert!(
nested.contains("zz/CLAUDE.md"),
"the deferred file must still be named: {}",
&nested[nested.len().saturating_sub(400)..]
);
assert!(
!nested.contains(&"z".repeat(200)),
"a sliver of the deferred file must not be inlined"
);
}
#[test]
fn the_repos_own_instructions_fit_the_cap() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(3)
.expect("crates/car-server-core is three levels below the repo root");
let claude_md = root.join("CLAUDE.md");
let Ok(raw) = std::fs::read_to_string(&claude_md) else {
return;
};
assert!(
raw.len() <= MAX_INSTRUCTIONS_BYTES,
"CAR's own CLAUDE.md is {} bytes and MAX_INSTRUCTIONS_BYTES is {}, so the \
coder working on this repo is silently losing the tail of its own rules. \
Raise the constant (and read its doc comment first) — do not delete this test.",
raw.len(),
MAX_INSTRUCTIONS_BYTES
);
}
#[test]
fn truncation_drops_the_tail_it_claims_to() {
let text = format!(
"{}\n## Project conventions (hard rules)\nno feature flags",
"x".repeat(100)
);
let cut = truncate_note(&text, 50);
assert!(!cut.contains("hard rules"), "the tail really is discarded");
assert!(
cut.contains("[truncated: 50 of"),
"and the loss is announced"
);
}
}