#![allow(dead_code)]
pub mod archive;
pub mod cli;
pub mod distiller;
#[cfg(feature = "relay")]
pub mod gossip;
pub mod injection;
pub mod merger;
pub mod store;
pub mod trust;
use std::sync::atomic::{AtomicU64, Ordering};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KnowledgeCategory {
BestPractice,
Technique,
WorkflowPattern,
Personal,
}
impl KnowledgeCategory {
pub fn is_shareable(&self) -> bool {
!matches!(self, Self::Personal)
}
pub fn is_allowed_by(&self, allow_list: &[String]) -> bool {
if !self.is_shareable() {
return false;
}
if allow_list.is_empty() {
return true; }
allow_list.iter().any(|s| s == self.label())
}
pub fn label(&self) -> &'static str {
match self {
Self::BestPractice => "best_practice",
Self::Technique => "technique",
Self::WorkflowPattern => "workflow",
Self::Personal => "personal",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KnowledgeUnit {
pub id: String,
pub scope: KnowledgeScope,
#[serde(default = "default_category")]
pub category: KnowledgeCategory,
pub content: KnowledgeContent,
pub evidence_count: u32,
pub confidence: f64,
pub source_peer: String,
pub originated_at: u64,
pub last_validated_at: u64,
pub propagation_count: u32,
pub version: u32,
}
fn default_category() -> KnowledgeCategory {
KnowledgeCategory::BestPractice
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub enum KnowledgeScope {
Universal,
Language(String),
Project(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum KnowledgeContent {
Pattern {
tool: String,
command_pattern: Option<String>,
preferred_action: String,
accept_rate: f64,
sample_count: u32,
conditions: Vec<String>,
},
ToolAccuracy {
tool: String,
total: u32,
correct: u32,
confidence_threshold: f64,
},
Temporal { description: String, strength: f64 },
Insight {
category: String,
severity: String,
summary: String,
suggestion: Option<String>,
},
PromotedRule { rule: String, source_type: String },
}
fn truncate_chars(s: &str, max_chars: usize) -> &str {
match s.char_indices().nth(max_chars) {
Some((idx, _)) => &s[..idx],
None => s,
}
}
pub fn semantic_key(unit: &KnowledgeUnit) -> String {
let scope_part = match &unit.scope {
KnowledgeScope::Universal => "universal".to_string(),
KnowledgeScope::Language(l) => format!("lang:{l}"),
KnowledgeScope::Project(p) => format!("proj:{p}"),
};
let content_part = match &unit.content {
KnowledgeContent::Pattern {
tool,
command_pattern,
..
} => {
let cmd = command_pattern.as_deref().unwrap_or("*");
format!("pattern:{tool}:{cmd}")
}
KnowledgeContent::ToolAccuracy { tool, .. } => format!("accuracy:{tool}"),
KnowledgeContent::Temporal { description, .. } => {
format!("temporal:{}", truncate_chars(description, 40))
}
KnowledgeContent::Insight {
category, summary, ..
} => {
format!("insight:{category}:{}", truncate_chars(summary, 40))
}
KnowledgeContent::PromotedRule { rule, .. } => {
format!("rule:{}", truncate_chars(rule, 40))
}
};
format!("{scope_part}/{content_part}")
}
#[derive(Debug, Clone, Default)]
pub struct SharingFilter {
pub allow_categories: Vec<String>,
pub exclude_tools: Vec<String>,
pub exclude_commands: Vec<String>,
}
impl SharingFilter {
pub fn from_config(cfg: &crate::config::HiveConfig) -> Self {
SharingFilter {
allow_categories: cfg.share_categories.clone(),
exclude_tools: cfg.exclude_tools.clone(),
exclude_commands: cfg.exclude_commands.clone(),
}
}
pub fn allows(&self, unit: &KnowledgeUnit) -> bool {
if !unit.category.is_allowed_by(&self.allow_categories) {
return false;
}
if let KnowledgeContent::Pattern {
ref tool,
ref command_pattern,
..
} = unit.content
{
if self.exclude_tools.iter().any(|t| t == tool) {
return false;
}
if let Some(cmd) = command_pattern {
if self
.exclude_commands
.iter()
.any(|exc| cmd.contains(exc.as_str()))
{
return false;
}
}
}
if let KnowledgeContent::ToolAccuracy { ref tool, .. } = unit.content {
if self.exclude_tools.iter().any(|t| t == tool) {
return false;
}
}
true
}
}
static KU_COUNTER: AtomicU64 = AtomicU64::new(0);
pub fn gen_ku_id() -> String {
let epoch = epoch_secs();
let seq = KU_COUNTER.fetch_add(1, Ordering::Relaxed);
format!("ku_{epoch}_{seq}")
}
pub fn epoch_secs() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
pub fn local_identity() -> String {
std::process::Command::new("hostname")
.output()
.ok()
.and_then(|o| {
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
if s.is_empty() { None } else { Some(s) }
})
.unwrap_or_else(|| "local".into())
}
#[cfg(feature = "relay")]
use std::sync::Mutex;
#[cfg(feature = "relay")]
static HIVE_BROADCAST_TX: Mutex<Option<std::sync::mpsc::Sender<u32>>> = Mutex::new(None);
#[cfg(feature = "relay")]
pub fn set_broadcast_channel(tx: std::sync::mpsc::Sender<u32>) {
if let Ok(mut guard) = HIVE_BROADCAST_TX.lock() {
*guard = Some(tx);
}
}
#[cfg(feature = "relay")]
pub fn signal_new_knowledge(count: u32) {
if let Ok(guard) = HIVE_BROADCAST_TX.lock() {
if let Some(ref tx) = *guard {
let _ = tx.send(count);
}
}
}
impl KnowledgeContent {
pub fn summary_line(&self) -> String {
match self {
Self::Pattern {
tool,
command_pattern,
preferred_action,
accept_rate,
..
} => {
let cmd = command_pattern.as_deref().unwrap_or("*");
format!(
"[{tool}, {cmd}] {preferred_action} ({:.0}%)",
accept_rate * 100.0
)
}
Self::ToolAccuracy {
tool,
total,
correct,
..
} => {
let pct = if *total > 0 {
(*correct as f64 / *total as f64) * 100.0
} else {
0.0
};
format!("[{tool}] accuracy {correct}/{total} ({pct:.0}%)")
}
Self::Temporal {
description,
strength,
..
} => {
format!("temporal: {description} (strength {strength:.2})")
}
Self::Insight {
category,
severity,
summary,
..
} => {
format!("[{severity}] {category}: {summary}")
}
Self::PromotedRule { rule, .. } => {
format!("rule: {rule}")
}
}
}
}
impl std::fmt::Display for KnowledgeScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Universal => write!(f, "universal"),
Self::Language(l) => write!(f, "language:{l}"),
Self::Project(p) => write!(f, "project:{p}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_pattern_unit(tool: &str, cmd: Option<&str>) -> KnowledgeUnit {
KnowledgeUnit {
id: "ku_1".into(),
scope: KnowledgeScope::Universal,
category: KnowledgeCategory::BestPractice,
content: KnowledgeContent::Pattern {
tool: tool.into(),
command_pattern: cmd.map(|s| s.into()),
preferred_action: "approve".into(),
accept_rate: 0.95,
sample_count: 20,
conditions: vec![],
},
evidence_count: 20,
confidence: 0.95,
source_peer: "peer-a".into(),
originated_at: 1000,
last_validated_at: 2000,
propagation_count: 0,
version: 1,
}
}
#[test]
fn semantic_key_pattern() {
let unit = sample_pattern_unit("Bash", Some("cargo test"));
assert_eq!(semantic_key(&unit), "universal/pattern:Bash:cargo test");
}
#[test]
fn semantic_key_pattern_no_command() {
let unit = sample_pattern_unit("Read", None);
assert_eq!(semantic_key(&unit), "universal/pattern:Read:*");
}
#[test]
fn semantic_key_with_scope() {
let mut unit = sample_pattern_unit("Bash", Some("cargo fmt"));
unit.scope = KnowledgeScope::Language("rust".into());
assert_eq!(semantic_key(&unit), "lang:rust/pattern:Bash:cargo fmt");
unit.scope = KnowledgeScope::Project("claudectl".into());
assert_eq!(semantic_key(&unit), "proj:claudectl/pattern:Bash:cargo fmt");
}
#[test]
fn semantic_key_accuracy() {
let unit = KnowledgeUnit {
id: "ku_2".into(),
scope: KnowledgeScope::Universal,
category: KnowledgeCategory::BestPractice,
content: KnowledgeContent::ToolAccuracy {
tool: "Bash".into(),
total: 100,
correct: 85,
confidence_threshold: 0.7,
},
evidence_count: 100,
confidence: 0.85,
source_peer: "peer-a".into(),
originated_at: 1000,
last_validated_at: 2000,
propagation_count: 0,
version: 1,
};
assert_eq!(semantic_key(&unit), "universal/accuracy:Bash");
}
#[test]
fn knowledge_unit_serde_roundtrip() {
let unit = sample_pattern_unit("Bash", Some("cargo test"));
let json = serde_json::to_string(&unit).unwrap();
let back: KnowledgeUnit = serde_json::from_str(&json).unwrap();
assert_eq!(back.id, "ku_1");
assert_eq!(back.confidence, 0.95);
assert_eq!(back.source_peer, "peer-a");
}
#[test]
fn content_summary_line() {
let unit = sample_pattern_unit("Bash", Some("cargo test"));
let line = unit.content.summary_line();
assert!(line.contains("Bash"));
assert!(line.contains("cargo test"));
assert!(line.contains("approve"));
}
#[test]
fn scope_display() {
assert_eq!(KnowledgeScope::Universal.to_string(), "universal");
assert_eq!(
KnowledgeScope::Language("rust".into()).to_string(),
"language:rust"
);
assert_eq!(
KnowledgeScope::Project("foo".into()).to_string(),
"project:foo"
);
}
#[test]
fn gen_ku_id_unique() {
let a = gen_ku_id();
let b = gen_ku_id();
assert_ne!(a, b);
assert!(a.starts_with("ku_"));
}
#[test]
fn semantic_key_multibyte_utf8_no_panic() {
let long_cjk = "这是一个用来测试多字节截断的临时模式描述文本超长";
let unit = KnowledgeUnit {
id: "ku_utf8".into(),
scope: KnowledgeScope::Universal,
category: KnowledgeCategory::BestPractice,
content: KnowledgeContent::Temporal {
description: long_cjk.to_string(),
strength: 0.9,
},
evidence_count: 5,
confidence: 0.9,
source_peer: "peer-a".into(),
originated_at: 1000,
last_validated_at: 2000,
propagation_count: 0,
version: 1,
};
let key = semantic_key(&unit);
assert!(key.starts_with("universal/temporal:"));
}
#[test]
fn semantic_key_emoji_no_panic() {
let emoji_text = "Error streak detected in tests 🎉🎊🎈🎁🎆🎇 more text here";
let unit = KnowledgeUnit {
id: "ku_emoji".into(),
scope: KnowledgeScope::Universal,
category: KnowledgeCategory::BestPractice,
content: KnowledgeContent::Insight {
category: "error_loop".into(),
severity: "warning".into(),
summary: emoji_text.to_string(),
suggestion: None,
},
evidence_count: 3,
confidence: 0.7,
source_peer: "peer-b".into(),
originated_at: 1000,
last_validated_at: 2000,
propagation_count: 0,
version: 1,
};
let key = semantic_key(&unit);
assert!(key.starts_with("universal/insight:error_loop:"));
}
}