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:
Kent Overstreet 2026-03-05 15:54:44 -05:00
parent a8aaadb0ad
commit 90d60894ed
5 changed files with 336 additions and 78 deletions

View file

@ -672,34 +672,59 @@ fn install_hook(home: &str, exe: &Path) -> Result<(), String> {
let hook_command = hook_binary.to_string_lossy().to_string();
// Check if hook already exists
let hooks = settings
.as_object_mut().ok_or("settings not an object")?
.entry("hooks")
// Navigate the nested structure: hooks.UserPromptSubmit[0].hooks[]
let obj = settings.as_object_mut().ok_or("settings not an object")?;
let hooks_obj = obj.entry("hooks")
.or_insert_with(|| serde_json::json!({}))
.as_object_mut().ok_or("hooks not an object")?
.entry("UserPromptSubmit")
.or_insert_with(|| serde_json::json!([]))
.as_object_mut().ok_or("hooks not an object")?;
let ups_array = hooks_obj.entry("UserPromptSubmit")
.or_insert_with(|| serde_json::json!([{"hooks": []}]))
.as_array_mut().ok_or("UserPromptSubmit not an array")?;
let already_installed = hooks.iter().any(|h| {
if ups_array.is_empty() {
ups_array.push(serde_json::json!({"hooks": []}));
}
let inner_hooks = ups_array[0]
.as_object_mut().ok_or("first element not an object")?
.entry("hooks")
.or_insert_with(|| serde_json::json!([]))
.as_array_mut().ok_or("inner hooks not an array")?;
// Remove load-memory.sh if present (replaced by memory-search)
let before_len = inner_hooks.len();
inner_hooks.retain(|h| {
let cmd = h.get("command").and_then(|c| c.as_str()).unwrap_or("");
!cmd.contains("load-memory")
});
if inner_hooks.len() < before_len {
eprintln!("Removed load-memory.sh hook (replaced by memory-search)");
}
// Check if memory-search hook already exists
let already_installed = inner_hooks.iter().any(|h| {
h.get("command").and_then(|c| c.as_str())
.is_some_and(|c| c.contains("memory-search"))
});
let mut changed = inner_hooks.len() < before_len;
if already_installed {
eprintln!("Hook already installed in {}", settings_path.display());
} else {
hooks.push(serde_json::json!({
inner_hooks.push(serde_json::json!({
"type": "command",
"command": hook_command,
"timeout": 10
}));
changed = true;
eprintln!("Hook installed: {}", hook_command);
}
if changed {
let json = serde_json::to_string_pretty(&settings)
.map_err(|e| format!("serialize settings: {}", e))?;
fs::write(&settings_path, json)
.map_err(|e| format!("write settings: {}", e))?;
eprintln!("Hook installed: {}", hook_command);
}
Ok(())