cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
// cli/node.rs — node subcommand handlers
|
|
|
|
|
//
|
|
|
|
|
// render, write, used, wrong, not-relevant, not-useful, gap,
|
|
|
|
|
// node-delete, node-rename, history, list-keys, list-edges,
|
|
|
|
|
// dump-json, lookup-bump, lookups.
|
|
|
|
|
|
|
|
|
|
use crate::store;
|
|
|
|
|
|
|
|
|
|
pub fn cmd_used(key: &[String]) -> Result<(), String> {
|
|
|
|
|
if key.is_empty() {
|
|
|
|
|
return Err("used requires a key".into());
|
|
|
|
|
}
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let key = key.join(" ");
|
|
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let resolved = store.resolve_key(&key)?;
|
|
|
|
|
store.mark_used(&resolved);
|
|
|
|
|
|
|
|
|
|
// Also strengthen edges to this node — conscious-tier delta.
|
|
|
|
|
const DELTA: f32 = 0.01;
|
|
|
|
|
let mut strengthened = 0;
|
|
|
|
|
for rel in &mut store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
|
|
|
|
if rel.source_key == resolved || rel.target_key == resolved {
|
|
|
|
|
let old = rel.strength;
|
|
|
|
|
rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95);
|
|
|
|
|
if (rel.strength - old).abs() > 0.001 {
|
|
|
|
|
rel.version += 1;
|
|
|
|
|
strengthened += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Marked '{}' as used (strengthened {} edges)", resolved, strengthened);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_wrong(key: &str, context: &[String]) -> Result<(), String> {
|
|
|
|
|
let ctx = if context.is_empty() { None } else { Some(context.join(" ")) };
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let resolved = store.resolve_key(key)?;
|
|
|
|
|
store.mark_wrong(&resolved, ctx.as_deref());
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Marked '{}' as wrong", resolved);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_not_relevant(key: &str) -> Result<(), String> {
|
|
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let resolved = store.resolve_key(key)?;
|
|
|
|
|
|
|
|
|
|
// Weaken all edges to this node — it was routed to incorrectly.
|
|
|
|
|
// Conscious-tier delta: 0.01 per edge.
|
|
|
|
|
const DELTA: f32 = -0.01;
|
|
|
|
|
let mut adjusted = 0;
|
|
|
|
|
for rel in &mut store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
|
|
|
|
if rel.source_key == resolved || rel.target_key == resolved {
|
|
|
|
|
let old = rel.strength;
|
|
|
|
|
rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95);
|
|
|
|
|
if (rel.strength - old).abs() > 0.001 {
|
|
|
|
|
rel.version += 1;
|
|
|
|
|
adjusted += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Not relevant: '{}' — weakened {} edges by {}", resolved, adjusted, DELTA.abs());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_not_useful(key: &str) -> Result<(), String> {
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
// no args to validate
|
|
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let resolved = store.resolve_key(key)?;
|
|
|
|
|
// Same as wrong but with clearer semantics: node content is bad, edges are fine.
|
|
|
|
|
store.mark_wrong(&resolved, Some("not-useful"));
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Not useful: '{}' — node weight reduced", resolved);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_gap(description: &[String]) -> Result<(), String> {
|
|
|
|
|
if description.is_empty() {
|
|
|
|
|
return Err("gap requires a description".into());
|
|
|
|
|
}
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let desc = description.join(" ");
|
|
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
store.record_gap(&desc);
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Recorded gap: {}", desc);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_list_keys(pattern: Option<&str>) -> Result<(), String> {
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
let g = store.build_graph();
|
|
|
|
|
|
|
|
|
|
if let Some(pat) = pattern {
|
|
|
|
|
let pat_lower = pat.to_lowercase();
|
|
|
|
|
let (prefix, suffix, middle) = if pat_lower.starts_with('*') && pat_lower.ends_with('*') {
|
|
|
|
|
(None, None, Some(pat_lower.trim_matches('*').to_string()))
|
|
|
|
|
} else if pat_lower.starts_with('*') {
|
|
|
|
|
(None, Some(pat_lower.trim_start_matches('*').to_string()), None)
|
|
|
|
|
} else if pat_lower.ends_with('*') {
|
|
|
|
|
(Some(pat_lower.trim_end_matches('*').to_string()), None, None)
|
|
|
|
|
} else {
|
|
|
|
|
(None, None, Some(pat_lower.clone()))
|
|
|
|
|
};
|
|
|
|
|
let mut keys: Vec<_> = store.nodes.keys()
|
|
|
|
|
.filter(|k| {
|
|
|
|
|
let kl = k.to_lowercase();
|
|
|
|
|
if let Some(ref m) = middle { kl.contains(m.as_str()) }
|
|
|
|
|
else if let Some(ref p) = prefix { kl.starts_with(p.as_str()) }
|
|
|
|
|
else if let Some(ref s) = suffix { kl.ends_with(s.as_str()) }
|
|
|
|
|
else { true }
|
|
|
|
|
})
|
|
|
|
|
.cloned()
|
|
|
|
|
.collect();
|
|
|
|
|
keys.sort();
|
|
|
|
|
for k in keys { println!("{}", k); }
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
crate::query_parser::run_query(&store, &g, "* | sort key asc")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_list_edges() -> Result<(), String> {
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
for rel in &store.relations {
|
|
|
|
|
println!("{}\t{}\t{:.2}\t{:?}",
|
|
|
|
|
rel.source_key, rel.target_key, rel.strength, rel.rel_type);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_dump_json() -> Result<(), String> {
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
let json = serde_json::to_string_pretty(&store)
|
|
|
|
|
.map_err(|e| format!("serialize: {}", e))?;
|
|
|
|
|
println!("{}", json);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_node_delete(key: &[String]) -> Result<(), String> {
|
|
|
|
|
if key.is_empty() {
|
|
|
|
|
return Err("node-delete requires a key".into());
|
|
|
|
|
}
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let key = key.join(" ");
|
|
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let resolved = store.resolve_key(&key)?;
|
|
|
|
|
store.delete_node(&resolved)?;
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Deleted '{}'", resolved);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> {
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
// args are positional, always valid if present
|
|
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let old_resolved = store.resolve_key(old_key)?;
|
|
|
|
|
store.rename_node(&old_resolved, new_key)?;
|
|
|
|
|
store.save()?;
|
|
|
|
|
println!("Renamed '{}' → '{}'", old_resolved, new_key);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_render(key: &[String]) -> Result<(), String> {
|
|
|
|
|
if key.is_empty() {
|
|
|
|
|
return Err("render requires a key".into());
|
|
|
|
|
}
|
|
|
|
|
let key = key.join(" ");
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
let bare = store::strip_md_suffix(&key);
|
|
|
|
|
|
|
|
|
|
let node = store.nodes.get(&bare)
|
|
|
|
|
.ok_or_else(|| format!("Node not found: {}", bare))?;
|
|
|
|
|
|
|
|
|
|
print!("{}", node.content);
|
|
|
|
|
|
|
|
|
|
// Show links so the graph is walkable
|
|
|
|
|
let mut neighbors: Vec<(&str, f32)> = Vec::new();
|
|
|
|
|
for r in &store.relations {
|
|
|
|
|
if r.deleted { continue; }
|
|
|
|
|
if r.source_key == bare {
|
|
|
|
|
neighbors.push((&r.target_key, r.strength));
|
|
|
|
|
} else if r.target_key == bare {
|
|
|
|
|
neighbors.push((&r.source_key, r.strength));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !neighbors.is_empty() {
|
|
|
|
|
neighbors.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
|
|
|
|
|
neighbors.dedup_by(|a, b| a.0 == b.0);
|
|
|
|
|
let total = neighbors.len();
|
|
|
|
|
let shown: Vec<_> = neighbors.iter().take(15)
|
|
|
|
|
.map(|(k, _)| format!("`poc-memory render {}`", k))
|
|
|
|
|
.collect();
|
|
|
|
|
print!("\n\n---\nLinks:");
|
|
|
|
|
for link in &shown {
|
|
|
|
|
println!("\n {}", link);
|
|
|
|
|
}
|
|
|
|
|
if total > 15 {
|
|
|
|
|
println!(" ... and {} more (`poc-memory graph link {}`)", total - 15, bare);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
|
|
|
|
if key.is_empty() {
|
|
|
|
|
return Err("history requires a key".into());
|
|
|
|
|
}
|
|
|
|
|
let raw_key = key.join(" ");
|
|
|
|
|
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
let key = store.resolve_key(&raw_key).unwrap_or(raw_key);
|
|
|
|
|
drop(store);
|
|
|
|
|
|
|
|
|
|
let path = store::nodes_path();
|
|
|
|
|
if !path.exists() {
|
|
|
|
|
return Err("No node log found".into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use std::io::BufReader;
|
|
|
|
|
let file = std::fs::File::open(&path)
|
|
|
|
|
.map_err(|e| format!("open {}: {}", path.display(), e))?;
|
|
|
|
|
let mut reader = BufReader::new(file);
|
|
|
|
|
|
|
|
|
|
let mut versions: Vec<store::Node> = Vec::new();
|
|
|
|
|
while let Ok(msg) = capnp::serialize::read_message(&mut reader, capnp::message::ReaderOptions::new()) {
|
|
|
|
|
let log = msg.get_root::<crate::memory_capnp::node_log::Reader>()
|
|
|
|
|
.map_err(|e| format!("read log: {}", e))?;
|
|
|
|
|
for node_reader in log.get_nodes()
|
|
|
|
|
.map_err(|e| format!("get nodes: {}", e))? {
|
|
|
|
|
let node = store::Node::from_capnp_migrate(node_reader)?;
|
|
|
|
|
if node.key == key {
|
|
|
|
|
versions.push(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if versions.is_empty() {
|
|
|
|
|
return Err(format!("No history found for '{}'", key));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eprintln!("{} versions of '{}':\n", versions.len(), key);
|
|
|
|
|
for node in &versions {
|
|
|
|
|
let ts = if node.timestamp > 0 && node.timestamp < 4_000_000_000 {
|
|
|
|
|
store::format_datetime(node.timestamp)
|
|
|
|
|
} else {
|
|
|
|
|
format!("(raw:{})", node.timestamp)
|
|
|
|
|
};
|
2026-03-17 18:00:58 -04:00
|
|
|
let deleted_marker = if node.deleted { " DELETED" } else { "" };
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
let content_len = node.content.len();
|
|
|
|
|
if full {
|
2026-03-17 18:00:58 -04:00
|
|
|
eprintln!("=== v{} {} {}{} w={:.3} {}b ===",
|
|
|
|
|
node.version, ts, node.provenance, deleted_marker, node.weight, content_len);
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
eprintln!("{}", node.content);
|
|
|
|
|
} else {
|
|
|
|
|
let preview = crate::util::first_n_chars(&node.content, 120);
|
|
|
|
|
let preview = preview.replace('\n', "\\n");
|
2026-03-17 18:00:58 -04:00
|
|
|
eprintln!(" v{:<3} {} {:24} w={:.3} {}b{}",
|
|
|
|
|
node.version, ts, node.provenance, node.weight, content_len, deleted_marker);
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
eprintln!(" {}", preview);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !full {
|
|
|
|
|
if let Some(latest) = versions.last() {
|
|
|
|
|
eprintln!("\n--- Latest content (v{}, {}) ---",
|
|
|
|
|
latest.version, latest.provenance);
|
|
|
|
|
print!("{}", latest.content);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_write(key: &[String]) -> Result<(), String> {
|
|
|
|
|
if key.is_empty() {
|
|
|
|
|
return Err("write requires a key (reads content from stdin)".into());
|
|
|
|
|
}
|
|
|
|
|
let raw_key = key.join(" ");
|
|
|
|
|
let mut content = String::new();
|
|
|
|
|
std::io::Read::read_to_string(&mut std::io::stdin(), &mut content)
|
|
|
|
|
.map_err(|e| format!("read stdin: {}", e))?;
|
|
|
|
|
|
|
|
|
|
if content.trim().is_empty() {
|
|
|
|
|
return Err("No content on stdin".into());
|
|
|
|
|
}
|
poc-memory: POC_MEMORY_DRY_RUN=1 for agent testing
All mutating commands (write, delete, rename, link-add, journal write,
used, wrong, not-useful, gap) check POC_MEMORY_DRY_RUN after argument
validation but before mutation. If set, process exits silently — agent
tool calls are visible in the LLM output so we can see what it tried
to do without applying changes.
Read commands (render, search, graph link, journal tail) work normally
in dry-run mode so agents can still explore the graph.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:09:56 -04:00
|
|
|
super::check_dry_run();
|
cli: extract node commands from main.rs into cli/node.rs
Move 15 node subcommand handlers (310 lines) out of main.rs:
render, write, used, wrong, not-relevant, not-useful, gap,
node-delete, node-rename, history, list-keys, list-edges,
dump-json, lookup-bump, lookups.
main.rs: 2518 → 2193 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:02:12 -04:00
|
|
|
|
|
|
|
|
let mut store = store::Store::load()?;
|
|
|
|
|
let key = store.resolve_key(&raw_key).unwrap_or(raw_key);
|
|
|
|
|
let result = store.upsert(&key, &content)?;
|
|
|
|
|
match result {
|
|
|
|
|
"unchanged" => println!("No change: '{}'", key),
|
|
|
|
|
"updated" => println!("Updated '{}' (v{})", key, store.nodes[&key].version),
|
|
|
|
|
_ => println!("Created '{}'", key),
|
|
|
|
|
}
|
|
|
|
|
if result != "unchanged" {
|
|
|
|
|
store.save()?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_lookup_bump(keys: &[String]) -> Result<(), String> {
|
|
|
|
|
if keys.is_empty() {
|
|
|
|
|
return Err("lookup-bump requires at least one key".into());
|
|
|
|
|
}
|
|
|
|
|
let keys: Vec<&str> = keys.iter().map(|s| s.as_str()).collect();
|
|
|
|
|
crate::lookups::bump_many(&keys)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn cmd_lookups(date: Option<&str>) -> Result<(), String> {
|
|
|
|
|
let date = date.map(|d| d.to_string())
|
|
|
|
|
.unwrap_or_else(|| chrono::Local::now().format("%Y-%m-%d").to_string());
|
|
|
|
|
|
|
|
|
|
let store = store::Store::load()?;
|
|
|
|
|
let keys: Vec<String> = store.nodes.values().map(|n| n.key.clone()).collect();
|
|
|
|
|
let resolved = crate::lookups::dump_resolved(&date, &keys)?;
|
|
|
|
|
|
|
|
|
|
if resolved.is_empty() {
|
|
|
|
|
println!("No lookups for {}", date);
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("Lookups for {}:", date);
|
|
|
|
|
for (key, count) in &resolved {
|
|
|
|
|
println!(" {:4} {}", count, key);
|
|
|
|
|
}
|
|
|
|
|
println!("\n{} distinct keys, {} total lookups",
|
|
|
|
|
resolved.len(),
|
|
|
|
|
resolved.iter().map(|(_, c)| *c as u64).sum::<u64>());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|