cursor: spatial memory navigation
Persistent cursor into the knowledge graph with navigation: - temporal: forward/back among same-type nodes by timestamp - hierarchical: up/down the digest tree (journal→daily→weekly→monthly) - spatial: graph neighbor display at every position The cursor file (~/.claude/memory/cursor) holds a single node key. Show displays: temporal arrows, hierarchy links, semantic neighbors, and full content. Date extraction from both timestamps and key names handles the mixed-timestamp data gracefully. This is the start of place cells — spatial awareness of position in your own knowledge.
This commit is contained in:
parent
abce1bba16
commit
7c1b96293f
3 changed files with 409 additions and 0 deletions
|
|
@ -199,6 +199,12 @@ EXAMPLES:
|
|||
#[command(subcommand, name = "graph")]
|
||||
GraphCmd(GraphCmd),
|
||||
|
||||
// ── Cursor (spatial memory) ──────────────────────────────────────
|
||||
|
||||
/// Navigate the memory graph with a persistent cursor
|
||||
#[command(subcommand)]
|
||||
Cursor(CursorCmd),
|
||||
|
||||
// ── Agents ────────────────────────────────────────────────────────
|
||||
|
||||
/// Agent and daemon operations
|
||||
|
|
@ -239,6 +245,27 @@ enum NodeCmd {
|
|||
Dump,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum CursorCmd {
|
||||
/// Show current cursor position with context
|
||||
Show,
|
||||
/// Set cursor to a node key
|
||||
Set {
|
||||
/// Node key
|
||||
key: Vec<String>,
|
||||
},
|
||||
/// Move cursor forward in time
|
||||
Forward,
|
||||
/// Move cursor backward in time
|
||||
Back,
|
||||
/// Move up the digest hierarchy (journal→daily→weekly→monthly)
|
||||
Up,
|
||||
/// Move down the digest hierarchy (to first child)
|
||||
Down,
|
||||
/// Clear the cursor
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum JournalCmd {
|
||||
/// Write a journal entry to the store
|
||||
|
|
@ -730,6 +757,9 @@ fn main() {
|
|||
=> cmd_organize(&term, threshold, key_only, anchor),
|
||||
},
|
||||
|
||||
// Cursor
|
||||
Command::Cursor(sub) => cmd_cursor(sub),
|
||||
|
||||
// Agent
|
||||
Command::Agent(sub) => match sub {
|
||||
AgentCmd::Daemon(sub) => cmd_daemon(sub),
|
||||
|
|
@ -2167,6 +2197,45 @@ fn cmd_load_context(stats: bool) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_cursor(sub: CursorCmd) -> Result<(), String> {
|
||||
match sub {
|
||||
CursorCmd::Show => {
|
||||
let store = store::Store::load()?;
|
||||
cursor::show(&store)
|
||||
}
|
||||
CursorCmd::Set { key } => {
|
||||
if key.is_empty() {
|
||||
return Err("cursor set requires a key".into());
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let store = store::Store::load()?;
|
||||
let bare = store::strip_md_suffix(&key);
|
||||
if !store.nodes.contains_key(&bare) {
|
||||
return Err(format!("Node not found: {}", bare));
|
||||
}
|
||||
cursor::set(&bare)?;
|
||||
cursor::show(&store)
|
||||
}
|
||||
CursorCmd::Forward => {
|
||||
let store = store::Store::load()?;
|
||||
cursor::move_temporal(&store, true)
|
||||
}
|
||||
CursorCmd::Back => {
|
||||
let store = store::Store::load()?;
|
||||
cursor::move_temporal(&store, false)
|
||||
}
|
||||
CursorCmd::Up => {
|
||||
let store = store::Store::load()?;
|
||||
cursor::move_up(&store)
|
||||
}
|
||||
CursorCmd::Down => {
|
||||
let store = store::Store::load()?;
|
||||
cursor::move_down(&store)
|
||||
}
|
||||
CursorCmd::Clear => cursor::clear(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_render(key: &[String]) -> Result<(), String> {
|
||||
if key.is_empty() {
|
||||
return Err("render requires a key".into());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue