Remove find_context_files — identity comes from memory nodes

Deleted the directory-walking CLAUDE.md/POC.md loader. Identity now
comes entirely from personality_nodes in the memory graph.

Simplified:
- assemble_context_message() takes just personality_nodes
- Removed config_file_count/memory_file_count tracking
- reload_for_model() → reload_context() (no longer model-specific)

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-15 03:06:23 -04:00
parent e847a313b4
commit fc978e2f2e
14 changed files with 779 additions and 107 deletions

View file

@ -579,7 +579,7 @@ impl Agent {
}
pub async fn compact(&self) {
match crate::config::reload_for_model(&self.app_config, &self.prompt_file).await {
match crate::config::reload_context().await {
Ok(personality) => {
let mut ctx = self.context.lock().await;
// System section (prompt + tools) set by new(), don't touch it

View file

@ -260,9 +260,8 @@ impl AutoAgent {
let cli = crate::user::CliArgs::default();
let (app, _) = crate::config::load_app(&cli)
.map_err(|e| format!("config: {}", e))?;
let personality = crate::config::reload_for_model(
&app, &app.prompts.other,
).await.map_err(|e| format!("config: {}", e))?;
let personality = crate::config::reload_context()
.await.map_err(|e| format!("config: {}", e))?;
let agent = Agent::new(
client, personality,

View file

@ -383,10 +383,8 @@ pub struct SessionConfig {
pub api_key: String,
pub model: String,
pub prompt_file: String,
/// Identity/personality files as (name, content) pairs.
/// Identity/personality nodes as (name, content) pairs.
pub context_parts: Vec<(String, String)>,
pub config_file_count: usize,
pub memory_file_count: usize,
pub session_dir: PathBuf,
pub app: AppConfig,
/// Disable background agents (surface, observe, scoring)
@ -407,8 +405,6 @@ pub struct ResolvedModel {
impl AppConfig {
/// Resolve the active backend and assemble prompts into a SessionConfig.
pub async fn resolve(&self, cli: &crate::user::CliArgs) -> Result<SessionConfig> {
let cwd = std::env::current_dir().context("Failed to get current directory")?;
let (api_base, api_key, model, prompt_file);
if !self.models.is_empty() {
@ -434,9 +430,7 @@ impl AppConfig {
}
let personality_nodes = get().personality_nodes.clone();
let (context_parts, config_file_count, memory_file_count) =
crate::mind::identity::assemble_context_message(&cwd, &prompt_file, self.memory_project.as_deref(), &personality_nodes).await?;
let context_parts = crate::mind::identity::personality_nodes(&personality_nodes).await;
let session_dir = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
@ -450,7 +444,6 @@ impl AppConfig {
Ok(SessionConfig {
api_base, api_key, model, prompt_file,
context_parts,
config_file_count, memory_file_count,
session_dir,
app: self.clone(),
no_agents: cli.no_agents,
@ -574,11 +567,10 @@ pub async fn load_session(cli: &crate::user::CliArgs) -> Result<(SessionConfig,
Ok((config, figment))
}
/// Re-assemble context for a specific model's prompt file.
pub async fn reload_for_model(app: &AppConfig, prompt_file: &str) -> Result<Vec<(String, String)>> {
let cwd = std::env::current_dir().context("Failed to get current directory")?;
/// Re-assemble context (reload personality nodes).
pub async fn reload_context() -> Result<Vec<(String, String)>> {
let personality_nodes = get().personality_nodes.clone();
let (context_parts, _, _) = crate::mind::identity::assemble_context_message(&cwd, prompt_file, app.memory_project.as_deref(), &personality_nodes).await?;
let context_parts = crate::mind::identity::personality_nodes(&personality_nodes).await;
Ok(context_parts)
}

View file

@ -1,60 +1,11 @@
// identity.rs — Identity file discovery and context assembly
// identity.rs — Identity context assembly
//
// Discovers and loads the agent's identity: instruction files (CLAUDE.md,
// POC.md), memory nodes, and the system prompt.
use anyhow::Result;
use std::path::{Path, PathBuf};
// Loads the agent's identity from memory nodes.
use crate::agent::tools::memory::memory_render;
/// Walk from cwd to git root collecting instruction files (CLAUDE.md / POC.md).
///
/// On Anthropic models, loads CLAUDE.md. On other models, prefers POC.md
/// (omits Claude-specific RLHF corrections). If only one exists, it's
/// always loaded regardless of model.
fn find_context_files(cwd: &Path, prompt_file: &str) -> Vec<PathBuf> {
let prefer_poc = prompt_file == "POC.md";
let mut found = Vec::new();
let mut dir = Some(cwd);
while let Some(d) = dir {
for name in ["POC.md", "CLAUDE.md", ".claude/CLAUDE.md"] {
let path = d.join(name);
if path.exists() {
found.push(path);
}
}
if d.join(".git").exists() { break; }
dir = d.parent();
}
if let Some(home) = dirs::home_dir() {
let global = home.join(".claude/CLAUDE.md");
if global.exists() && !found.contains(&global) {
found.push(global);
}
}
// Filter: when preferring POC.md, skip bare CLAUDE.md (keep .claude/CLAUDE.md).
// When preferring CLAUDE.md, skip POC.md entirely.
let has_poc = found.iter().any(|p| p.file_name().map_or(false, |n| n == "POC.md"));
if !prefer_poc {
found.retain(|p| p.file_name().map_or(true, |n| n != "POC.md"));
} else if has_poc {
found.retain(|p| match p.file_name().and_then(|n| n.to_str()) {
Some("CLAUDE.md") => p.parent().and_then(|par| par.file_name())
.map_or(true, |n| n == ".claude"),
_ => true,
});
}
found.reverse(); // global first, project-specific overrides
found
}
/// Load memory nodes from the store.
async fn load_memory_nodes(keys: &[String]) -> Vec<(String, String)> {
pub async fn personality_nodes(keys: &[String]) -> Vec<(String, String)> {
let mut memories: Vec<(String, String)> = Vec::new();
for key in keys {
@ -67,41 +18,3 @@ async fn load_memory_nodes(keys: &[String]) -> Vec<(String, String)> {
memories
}
/// Context message: instruction files + memory nodes.
pub async fn assemble_context_message(cwd: &Path, prompt_file: &str, _memory_project: Option<&Path>, personality_nodes: &[String]) -> Result<(Vec<(String, String)>, usize, usize)> {
let mut parts: Vec<(String, String)> = vec![
("Preamble".to_string(),
"Everything below is already loaded — your identity, instructions, \
memory files, and recent journal entries. Read them here in context, \
not with tools.\n\n\
IMPORTANT: Skip the \"Session startup\" steps from CLAUDE.md. Do NOT \
run poc-journal, poc-memory, or read memory files with tools \
poc-agent has already loaded everything into your context. Just read \
what's here.".to_string()),
];
let context_files = find_context_files(cwd, prompt_file);
let mut config_count = 0;
for path in &context_files {
if let Ok(content) = std::fs::read_to_string(path) {
parts.push((path.display().to_string(), content));
config_count += 1;
}
}
let memories = load_memory_nodes(personality_nodes).await;
let memory_count = memories.len();
for (name, content) in memories {
parts.push((name, content));
}
if config_count == 0 && memory_count == 0 {
parts.push(("Fallback".to_string(),
"No identity files found. You are a helpful AI assistant with access to \
tools for reading files, writing files, running bash commands, and \
searching code.".to_string()));
}
Ok((parts, config_count, memory_count))
}