forked from kent/consciousness
Replace complex context_groups (with ContextGroup struct, ContextSource enum, labels, keys arrays) with simple string lists: - personality_nodes: loaded into main session context - agent_nodes: loaded into subconscious agent context Removed ~200 lines of code. The distinction between session and agent context is now just which list you're in, not a per-group flag. Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
107 lines
3.8 KiB
Rust
107 lines
3.8 KiB
Rust
// identity.rs — Identity file discovery and 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};
|
|
|
|
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)> {
|
|
let mut memories: Vec<(String, String)> = Vec::new();
|
|
|
|
for key in keys {
|
|
if let Ok(c) = memory_render(None, key, Some(true)).await {
|
|
if !c.trim().is_empty() {
|
|
memories.push((key.clone(), c));
|
|
}
|
|
}
|
|
}
|
|
|
|
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))
|
|
}
|