use schemars::JsonSchema;
use serde::Deserialize;
use std::fmt::Write;
pub const VALID_THOUGHT_TYPES: &[&str] = &["analytical", "critical", "synthesis", "validation"];
#[derive(Deserialize, JsonSchema, Clone)]
pub struct ThinkInput {
pub thought: String,
#[serde(rename = "nextThoughtNeeded")]
pub next_thought_needed: bool,
#[serde(rename = "thoughtNumber")]
pub thought_number: u32,
#[serde(rename = "totalThoughts")]
pub total_thoughts: u32,
#[serde(rename = "isRevision", skip_serializing_if = "Option::is_none")]
pub is_revision: Option<bool>,
#[serde(rename = "revisesThought", skip_serializing_if = "Option::is_none")]
pub revises_thought: Option<u32>,
#[serde(rename = "branchFromThought", skip_serializing_if = "Option::is_none")]
pub branch_from_thought: Option<u32>,
#[serde(rename = "branchId", skip_serializing_if = "Option::is_none")]
pub branch_id: Option<String>,
#[serde(rename = "needsMoreThoughts", skip_serializing_if = "Option::is_none")]
pub needs_more_thoughts: Option<bool>,
#[serde(rename = "customLens", skip_serializing_if = "Option::is_none")]
pub custom_lens: Option<String>,
#[serde(rename = "thoughtType", skip_serializing_if = "Option::is_none")]
pub thought_type: Option<String>,
#[serde(rename = "confidence", skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
}
#[derive(Clone)]
pub struct ThoughtData {
pub thought: String,
pub thought_number: u32,
pub total_thoughts: u32,
pub next_thought_needed: bool,
pub is_revision: Option<bool>,
pub revises_thought: Option<u32>,
pub branch_from_thought: Option<u32>,
pub branch_id: Option<String>,
pub needs_more_thoughts: Option<bool>,
pub custom_lens: Option<String>,
pub thought_type: Option<String>,
pub confidence: Option<f32>,
}
pub struct ThinkResult {
pub formatted_output: String,
pub is_error: bool,
}
impl From<ThinkInput> for ThoughtData {
fn from(input: ThinkInput) -> Self {
let adjusted_total = if input.thought_number > input.total_thoughts {
input.thought_number
} else {
input.total_thoughts
};
ThoughtData {
thought: input.thought,
thought_number: input.thought_number,
total_thoughts: adjusted_total,
next_thought_needed: input.next_thought_needed,
is_revision: input.is_revision,
revises_thought: input.revises_thought,
branch_from_thought: input.branch_from_thought,
branch_id: input.branch_id,
needs_more_thoughts: input.needs_more_thoughts,
custom_lens: input.custom_lens,
thought_type: input.thought_type,
confidence: input.confidence,
}
}
}
#[must_use]
pub fn process_thinking(input: ThinkInput) -> ThinkResult {
if input.thought_number == 0 || input.total_thoughts == 0 {
return ThinkResult {
formatted_output: "Error: Thought numbers must be positive".to_string(),
is_error: true,
};
}
if let Some(ref thought_type) = input.thought_type {
if !VALID_THOUGHT_TYPES.contains(&thought_type.as_str()) {
return ThinkResult {
formatted_output: format!(
"Error: Invalid thought type '{thought_type}'. Valid types are: analytical, critical, synthesis, validation"
),
is_error: true,
};
}
}
let mut thought_data = ThoughtData::from(input);
if thought_data
.thought_type
.as_ref()
.is_some_and(|t| t == "critical")
{
thought_data.thought = apply_critical_analysis(&thought_data.thought);
}
if let Some(ref lens) = thought_data.custom_lens {
thought_data.thought = apply_custom_lens(&thought_data.thought, lens);
}
if thought_data
.thought_type
.as_ref()
.is_some_and(|t| t == "critical")
&& thought_data.thought.contains("⚠️")
{
thought_data.confidence = thought_data
.confidence
.map(|c| (c * 0.8).max(0.1))
.or(Some(0.7));
}
let formatted_output = format_thought(&thought_data);
ThinkResult {
formatted_output,
is_error: false,
}
}
fn format_thought(thought_data: &ThoughtData) -> String {
let is_revision = thought_data.is_revision.unwrap_or(false);
let is_branch = thought_data.branch_from_thought.is_some();
let _is_critical = thought_data
.thought_type
.as_ref()
.is_some_and(|t| t == "critical");
let icon = match thought_data.thought_type.as_deref() {
Some("critical") => "🔍",
Some("synthesis") => "🔗",
Some("validation") => "✓",
Some("analytical") | None => {
if is_revision {
"🔄"
} else if is_branch {
"🌿"
} else if thought_data.custom_lens.is_some() {
"🎯"
} else {
"💭"
}
}
Some(_) => "💭", };
let mut metadata = format!(
"{icon} {}/{}",
thought_data.thought_number, thought_data.total_thoughts
);
if let Some(thought_type) = &thought_data.thought_type {
let _ = write!(metadata, " [{thought_type}]");
}
if let Some(lens) = &thought_data.custom_lens {
let _ = write!(metadata, " 🔎{lens}");
}
if let Some(confidence) = thought_data.confidence {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let confidence_pct = (confidence * 100.0) as u32;
let _ = write!(metadata, " {confidence_pct}%");
}
if is_revision {
if let Some(revises) = thought_data.revises_thought {
let _ = write!(metadata, " ↺#{revises}");
}
}
if is_branch {
if let Some(branch_from) = thought_data.branch_from_thought {
let _ = write!(metadata, " └#{branch_from}");
}
if let Some(branch_id) = &thought_data.branch_id {
let _ = write!(metadata, "[{branch_id}]");
}
}
if thought_data.needs_more_thoughts.unwrap_or(false) {
metadata.push_str(" (+)");
}
let formatted_thought = thought_data.thought.clone();
format!("{metadata} | {formatted_thought}")
}
fn apply_critical_analysis(thought: &str) -> String {
let mut analysis = thought.to_string();
if thought.contains("assume") || thought.contains("probably") || thought.contains("might") {
analysis.push_str(" [⚠️ ASSUMPTION DETECTED: Verify this claim]");
}
if thought.contains("all") || thought.contains("never") || thought.contains("always") {
analysis.push_str(" [⚠️ ABSOLUTE STATEMENT: Consider edge cases]");
}
if thought.contains("obviously") || thought.contains("clearly") {
analysis.push_str(" [⚠️ IMPLICIT BIAS: What might not be obvious?]");
}
analysis
}
fn apply_custom_lens(thought: &str, lens: &str) -> String {
let mut filtered = thought.to_string();
match lens.to_lowercase().as_str() {
lens if lens.contains("rust") => {
if thought.contains("memory") || thought.contains("pointer") {
filtered.push_str(" [🔎 RUST: Consider ownership and borrowing rules]");
}
if thought.contains("error") || thought.contains("fail") {
filtered.push_str(" [🔎 RUST: Use Result<T, E> for error handling]");
}
}
lens if lens.contains("security") => {
if thought.contains("input") || thought.contains("user") {
filtered.push_str(" [🔎 SECURITY: Validate and sanitize all inputs]");
}
if thought.contains("auth") || thought.contains("token") {
filtered.push_str(" [🔎 SECURITY: Ensure proper authentication]");
}
}
lens if lens.contains("performance") => {
if thought.contains("loop") || thought.contains("iterate") {
filtered.push_str(" [🔎 PERFORMANCE: Check algorithmic complexity]");
}
if thought.contains("memory") || thought.contains("allocat") {
filtered.push_str(" [🔎 PERFORMANCE: Monitor memory usage]");
}
}
_ => {
let _ = write!(filtered, " [🔎 {lens}: Applied custom perspective]");
}
}
filtered
}
#[allow(dead_code)]
fn wrap_text(text: &str, width: usize) -> Vec<String> {
let mut lines = Vec::new();
let words: Vec<&str> = text.split_whitespace().collect();
let mut current_line = String::new();
for word in words {
if current_line.len() + word.len() + 1 > width && !current_line.is_empty() {
lines.push(current_line.clone());
current_line.clear();
}
if !current_line.is_empty() {
current_line.push(' ');
}
current_line.push_str(word);
}
if !current_line.is_empty() {
lines.push(current_line);
}
if lines.is_empty() {
lines.push(String::new());
}
lines
}
#[cfg(test)]
mod tests {
use super::*;
fn create_basic_input() -> ThinkInput {
ThinkInput {
thought: "Test thought".to_string(),
next_thought_needed: true,
thought_number: 1,
total_thoughts: 3,
is_revision: None,
revises_thought: None,
branch_from_thought: None,
branch_id: None,
needs_more_thoughts: None,
custom_lens: None,
thought_type: None,
confidence: None,
}
}
#[test]
fn test_basic_thinking() {
let input = create_basic_input();
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("💭 1/3 | Test thought"));
}
#[test]
fn test_analytical_thought_type() {
let mut input = create_basic_input();
input.thought_type = Some("analytical".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(
result
.formatted_output
.contains("💭 1/3 [analytical] | Test thought")
);
}
#[test]
fn test_critical_thought_type() {
let mut input = create_basic_input();
input.thought = "This will always work perfectly".to_string();
input.thought_type = Some("critical".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🔍"));
assert!(result.formatted_output.contains("[critical]"));
assert!(result.formatted_output.contains("ABSOLUTE STATEMENT"));
}
#[test]
fn test_synthesis_thought_type() {
let mut input = create_basic_input();
input.thought_type = Some("synthesis".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(
result
.formatted_output
.contains("🔗 1/3 [synthesis] | Test thought")
);
}
#[test]
fn test_validation_thought_type() {
let mut input = create_basic_input();
input.thought_type = Some("validation".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(
result
.formatted_output
.contains("✓ 1/3 [validation] | Test thought")
);
}
#[test]
fn test_invalid_thought_type() {
let mut input = create_basic_input();
input.thought_type = Some("invalid_type".to_string());
let result = process_thinking(input);
assert!(result.is_error);
assert!(result.formatted_output.contains("Invalid thought type"));
}
#[test]
fn test_revision_tracking() {
let mut input = create_basic_input();
input.thought_number = 3;
input.is_revision = Some(true);
input.revises_thought = Some(2);
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🔄"));
assert!(result.formatted_output.contains("↺#2"));
}
#[test]
fn test_branching_thoughts() {
let mut input = create_basic_input();
input.thought_number = 4;
input.total_thoughts = 5;
input.branch_from_thought = Some(3);
input.branch_id = Some("alt-1".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🌿"));
assert!(result.formatted_output.contains("└#3[alt-1]"));
}
#[test]
fn test_custom_lens_rust() {
let mut input = create_basic_input();
input.thought = "Need to handle memory allocation here".to_string();
input.custom_lens = Some("rust".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🎯"));
assert!(result.formatted_output.contains("🔎rust"));
assert!(result.formatted_output.contains("ownership and borrowing"));
}
#[test]
fn test_custom_lens_security() {
let mut input = create_basic_input();
input.thought = "Processing user input from the form".to_string();
input.custom_lens = Some("security".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🔎security"));
assert!(result.formatted_output.contains("Validate and sanitize"));
}
#[test]
fn test_custom_lens_performance() {
let mut input = create_basic_input();
input.thought = "Need to iterate through all items".to_string();
input.custom_lens = Some("performance".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🔎performance"));
assert!(result.formatted_output.contains("algorithmic complexity"));
}
#[test]
fn test_confidence_tracking() {
let mut input = create_basic_input();
input.confidence = Some(0.85);
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("85%"));
}
#[test]
fn test_needs_more_thoughts() {
let mut input = create_basic_input();
input.needs_more_thoughts = Some(true);
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("(+)"));
}
#[test]
fn test_auto_adjust_total_thoughts() {
let mut input = create_basic_input();
input.thought_number = 5;
input.total_thoughts = 3;
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("5/5"));
}
#[test]
fn test_zero_thought_number_error() {
let mut input = create_basic_input();
input.thought_number = 0;
let result = process_thinking(input);
assert!(result.is_error);
assert!(result.formatted_output.contains("must be positive"));
}
#[test]
fn test_zero_total_thoughts_error() {
let mut input = create_basic_input();
input.total_thoughts = 0;
let result = process_thinking(input);
assert!(result.is_error);
assert!(result.formatted_output.contains("must be positive"));
}
#[test]
fn test_critical_analysis_assumptions() {
let mut input = create_basic_input();
input.thought = "I assume this will probably work".to_string();
input.thought_type = Some("critical".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("ASSUMPTION DETECTED"));
}
#[test]
fn test_critical_analysis_absolutes() {
let mut input = create_basic_input();
input.thought = "This never fails".to_string();
input.thought_type = Some("critical".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("ABSOLUTE STATEMENT"));
}
#[test]
fn test_critical_analysis_bias() {
let mut input = create_basic_input();
input.thought = "obviously this is the best approach".to_string();
input.thought_type = Some("critical".to_string());
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("IMPLICIT BIAS"));
}
#[test]
fn test_critical_confidence_adjustment() {
let mut input = create_basic_input();
input.thought = "This approach assumes perfect conditions".to_string();
input.thought_type = Some("critical".to_string());
input.confidence = Some(0.9);
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("72%"));
}
#[test]
fn test_complex_combined_features() {
let input = ThinkInput {
thought: "Revising my security analysis of the authentication system".to_string(),
next_thought_needed: true,
thought_number: 7,
total_thoughts: 5, is_revision: Some(true),
revises_thought: Some(4),
branch_from_thought: None,
branch_id: None,
needs_more_thoughts: Some(true),
custom_lens: Some("security".to_string()),
thought_type: Some("critical".to_string()),
confidence: Some(0.75),
};
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("🔍")); assert!(result.formatted_output.contains("7/7")); assert!(result.formatted_output.contains("🔎security"));
assert!(result.formatted_output.contains("75%"));
assert!(result.formatted_output.contains("↺#4"));
assert!(result.formatted_output.contains("(+)"));
}
#[test]
fn test_text_wrapping() {
let text = "This is a very long line that should be wrapped at the appropriate width to ensure readability";
let lines = wrap_text(text, 20);
assert!(lines.len() > 1);
assert!(lines.iter().all(|line| line.len() <= 20));
}
#[test]
fn test_empty_thought() {
let mut input = create_basic_input();
input.thought = "".to_string();
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("💭 1/3 | "));
}
#[test]
fn test_special_characters_in_thought() {
let mut input = create_basic_input();
input.thought = "Testing with special chars: !@#$%^&*(){}[]|\\<>?".to_string();
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("!@#$%^&*(){}[]|\\<>?"));
}
#[test]
fn test_unicode_in_thought() {
let mut input = create_basic_input();
input.thought = "Testing with unicode: 你好 🌍 café ñ".to_string();
let result = process_thinking(input);
assert!(!result.is_error);
assert!(result.formatted_output.contains("你好 🌍 café ñ"));
}
}