config-driven context loading, consolidate hooks, add docs
Move the hardcoded context priority groups from cmd_load_context() into the config file as [context.NAME] sections. Add journal_days and journal_max settings. The config parser handles section headers with ordered group preservation. Consolidate load-memory.sh into the memory-search binary — it now handles both session-start context loading (first prompt) and ambient search (subsequent prompts), eliminating the shell script. Update install_hook() to reference ~/.cargo/bin/memory-search and remove the old load-memory.sh entry from settings.json. Add end-user documentation (doc/README.md) covering installation, configuration, all commands, hook mechanics, and notes for AI assistants using the system. Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
parent
a8aaadb0ad
commit
90d60894ed
5 changed files with 336 additions and 78 deletions
|
|
@ -8,6 +8,12 @@ use std::sync::OnceLock;
|
|||
|
||||
static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContextGroup {
|
||||
pub label: String,
|
||||
pub keys: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
/// Display name for the human user in transcripts/prompts.
|
||||
|
|
@ -20,6 +26,12 @@ pub struct Config {
|
|||
pub projects_dir: PathBuf,
|
||||
/// Core node keys that should never be decayed/deleted.
|
||||
pub core_nodes: Vec<String>,
|
||||
/// How many days of journal to include in load-context.
|
||||
pub journal_days: u32,
|
||||
/// Max journal entries to include in load-context.
|
||||
pub journal_max: usize,
|
||||
/// Ordered context groups for session-start loading.
|
||||
pub context_groups: Vec<ContextGroup>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
|
@ -31,6 +43,11 @@ impl Default for Config {
|
|||
data_dir: home.join(".claude/memory"),
|
||||
projects_dir: home.join(".claude/projects"),
|
||||
core_nodes: vec!["identity.md".to_string()],
|
||||
journal_days: 7,
|
||||
journal_max: 20,
|
||||
context_groups: vec![
|
||||
ContextGroup { label: "identity".into(), keys: vec!["identity.md".into()] },
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,16 +67,56 @@ impl Config {
|
|||
return config;
|
||||
};
|
||||
|
||||
// Simple TOML parser — we only need flat key = "value" pairs.
|
||||
// Simple TOML parser: flat key=value pairs + [context.NAME] sections.
|
||||
let mut context_groups: Vec<ContextGroup> = Vec::new();
|
||||
let mut current_section: Option<String> = None;
|
||||
let mut current_label: Option<String> = None;
|
||||
let mut current_keys: Vec<String> = Vec::new();
|
||||
let mut saw_context = false;
|
||||
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section header: [context.NAME]
|
||||
if line.starts_with('[') && line.ends_with(']') {
|
||||
// Flush previous context section
|
||||
if let Some(name) = current_section.take() {
|
||||
let label = current_label.take()
|
||||
.unwrap_or_else(|| name.replace('_', " "));
|
||||
context_groups.push(ContextGroup { label, keys: std::mem::take(&mut current_keys) });
|
||||
}
|
||||
|
||||
let section = &line[1..line.len()-1];
|
||||
if let Some(name) = section.strip_prefix("context.") {
|
||||
current_section = Some(name.to_string());
|
||||
saw_context = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some((key, value)) = line.split_once('=') else { continue };
|
||||
let key = key.trim();
|
||||
let value = value.trim().trim_matches('"');
|
||||
|
||||
// Inside a [context.X] section
|
||||
if current_section.is_some() {
|
||||
match key {
|
||||
"keys" => {
|
||||
current_keys = value.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
"label" => current_label = Some(value.to_string()),
|
||||
_ => {}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Top-level keys
|
||||
match key {
|
||||
"user_name" => config.user_name = value.to_string(),
|
||||
"assistant_name" => config.assistant_name = value.to_string(),
|
||||
|
|
@ -71,10 +128,27 @@ impl Config {
|
|||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
"journal_days" => {
|
||||
if let Ok(d) = value.parse() { config.journal_days = d; }
|
||||
}
|
||||
"journal_max" => {
|
||||
if let Ok(m) = value.parse() { config.journal_max = m; }
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush final section
|
||||
if let Some(name) = current_section.take() {
|
||||
let label = current_label.take()
|
||||
.unwrap_or_else(|| name.replace('_', " "));
|
||||
context_groups.push(ContextGroup { label, keys: current_keys });
|
||||
}
|
||||
|
||||
if saw_context {
|
||||
config.context_groups = context_groups;
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue