forked from kent/consciousness
kill cmd_graph, cmd_organize
This commit is contained in:
parent
11f2d5b169
commit
1f6bfb5915
3 changed files with 2 additions and 139 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
120
src/cli/graph.rs
120
src/cli/graph.rs
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
19
src/main.rs
19
src/main.rs
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue