kill cmd_graph, cmd_organize

This commit is contained in:
Kent Overstreet 2026-04-12 23:19:28 -04:00
parent 11f2d5b169
commit 1f6bfb5915
3 changed files with 2 additions and 139 deletions

View file

@ -1,6 +1,7 @@
// cli/admin.rs — admin subcommand handlers
use crate::store;
fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -> Result<(), String> {
let path = data_dir.join(name);
if !path.exists() {
@ -11,7 +12,6 @@ fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -
Ok(())
}
pub fn cmd_init() -> Result<(), String> {
let cfg = crate::config::get();

View file

@ -4,19 +4,7 @@
// link, link-add, link-impact, link-audit, cap-degree,
// normalize-strengths, trace, spectral-*, organize, communities.
use crate::{store, graph};
use crate::store::StoreView;
pub fn cmd_graph() -> Result<(), String> {
let store = store::Store::load()?;
let g = store.build_graph();
println!("Graph: {} nodes, {} edges, {} communities",
g.nodes().len(), g.edge_count(), g.community_count());
println!("σ={:.2} α={:.2} gini={:.3} cc={:.4}",
g.small_world_sigma(), g.degree_power_law_exponent(),
g.degree_gini(), g.avg_clustering_coefficient());
Ok(())
}
use crate::store;
pub fn cmd_cap_degree(max_deg: usize) -> Result<(), String> {
let mut store = store::Store::load()?;
@ -91,111 +79,6 @@ pub fn cmd_trace(key: &[String]) -> Result<(), String> {
Ok(())
}
pub fn cmd_organize(term: &str, key_only: bool, create_anchor: bool) -> Result<(), String> {
let mut store = store::Store::load()?;
// Step 1: find all non-deleted nodes matching the term
let term_lower = term.to_lowercase();
let mut topic_nodes: Vec<(String, String)> = Vec::new(); // (key, content)
let skip_prefixes = ["_", "deep-index#", "facts-", "irc-history#"];
for (key, node) in &store.nodes {
if node.deleted { continue; }
// Skip episodic/digest nodes — use NodeType, not key prefix
if node.node_type != crate::store::NodeType::Semantic { continue; }
let key_matches = key.to_lowercase().contains(&term_lower);
let content_matches = !key_only && node.content.to_lowercase().contains(&term_lower);
if !key_matches && !content_matches { continue; }
if skip_prefixes.iter().any(|p| key.starts_with(p)) { continue; }
topic_nodes.push((key.clone(), node.content.clone()));
}
if topic_nodes.is_empty() {
println!("No topic nodes found matching '{}'", term);
return Ok(());
}
topic_nodes.sort_by(|a, b| a.0.cmp(&b.0));
println!("=== Organize: '{}' ===", term);
println!("Found {} topic nodes:\n", topic_nodes.len());
for (key, content) in &topic_nodes {
let lines = content.lines().count();
let words = content.split_whitespace().count();
println!(" {:60} {:>4} lines {:>5} words", key, lines, words);
}
// Step 2: check connectivity within cluster
let g = store.build_graph();
println!("=== Connectivity ===\n");
// Pick hub by intra-cluster connectivity, not overall degree
let cluster_keys: std::collections::HashSet<&str> = topic_nodes.iter()
.filter(|(k,_)| store.nodes.contains_key(k.as_str()))
.map(|(k,_)| k.as_str())
.collect();
let mut best_hub: Option<(&str, usize)> = None;
for key in &cluster_keys {
let intra_degree = g.neighbor_keys(key).iter()
.filter(|n| cluster_keys.contains(*n))
.count();
if best_hub.is_none() || intra_degree > best_hub.unwrap().1 {
best_hub = Some((key, intra_degree));
}
}
if let Some((hub, deg)) = best_hub {
println!(" Hub: {} (degree {})", hub, deg);
let hub_nbrs = g.neighbor_keys(hub);
let mut unlinked = Vec::new();
for (key, _) in &topic_nodes {
if key == hub { continue; }
if store.nodes.get(key.as_str()).is_none() { continue; }
if !hub_nbrs.contains(key.as_str()) {
unlinked.push(key.clone());
}
}
if unlinked.is_empty() {
println!(" All cluster nodes connected to hub ✓");
} else {
println!(" NOT linked to hub:");
for key in &unlinked {
println!(" {} → needs link to {}", key, hub);
}
}
}
// Step 4: anchor node
if create_anchor {
println!("\n=== Anchor node ===\n");
if store.nodes.contains_key(term) && !store.nodes[term].deleted {
println!(" Anchor '{}' already exists ✓", term);
} else {
let desc = format!("Anchor node for '{}' search term", term);
store.upsert(term, &desc)?;
let anchor_uuid = store.nodes.get(term).unwrap().uuid;
for (key, _) in &topic_nodes {
if store.nodes.get(key.as_str()).is_none() { continue; }
let target_uuid = store.nodes[key.as_str()].uuid;
let rel = store::new_relation(
anchor_uuid, target_uuid,
store::RelationType::Link, 0.8,
term, key,
);
store.add_relation(rel)?;
}
println!(" Created anchor '{}' with {} links", term, topic_nodes.len());
}
}
store.save()?;
Ok(())
}
/// Show communities sorted by isolation (most isolated first).
/// Useful for finding poorly-integrated knowledge clusters that need
/// organize agents aimed at them.
@ -207,4 +90,3 @@ pub fn cmd_communities(top_n: usize, min_size: usize) -> Result<(), String> {
print!("{}", result);
Ok(())
}

View file

@ -270,22 +270,6 @@ enum GraphCmd {
#[arg(long, default_value_t = 2)]
min_size: usize,
},
/// Show graph structure overview
Overview,
/// Diagnose duplicate/overlapping nodes for a topic cluster
Organize {
/// Search term (matches node keys; also content unless --key-only)
term: String,
/// Similarity threshold for pair reporting (default: 0.4)
#[arg(long, default_value_t = 0.4)]
threshold: f32,
/// Only match node keys, not content
#[arg(long)]
key_only: bool,
/// Create anchor node for the search term and link to cluster
#[arg(long)]
anchor: bool,
},
}
#[derive(Subcommand)]
@ -480,9 +464,6 @@ impl Run for GraphCmd {
Self::NormalizeStrengths { apply } => cli::graph::cmd_normalize_strengths(apply),
Self::Trace { key } => cli::graph::cmd_trace(&key),
Self::Communities { top_n, min_size } => cli::graph::cmd_communities(top_n, min_size),
Self::Overview => cli::graph::cmd_graph(),
Self::Organize { term, key_only, anchor, .. }
=> cli::graph::cmd_organize(&term, key_only, anchor),
}
}
}