Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,4 @@ Parse: `file:line:col` → location | 💡 → how to fix | Exit 0/1 → pass/fa
- ❌ Full scan per edit → ✅ Scope to file
- ❌ Fix symptom (`if (x) { x.y }`) → ✅ Root cause (`x?.y`)
````
Use 'bd' for task tracking
Original file line number Diff line number Diff line change
Expand Up @@ -503,11 +503,10 @@ mod collaboration_and_attribution_tests {
for analysis in &analyses {
for file_op in &analysis.file_operations {
total_operations += 1;
if file_op.agent_context.is_some() {
if let Some(agent_context) = &file_op.agent_context {
operations_with_context += 1;

// Verify the agent context is reasonable
let agent_context = file_op.agent_context.as_ref().unwrap();
assert!(
!agent_context.is_empty(),
"Agent context should not be empty"
Expand Down
49 changes: 47 additions & 2 deletions crates/terraphim_agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ fn show_usage_info() {
println!("Terraphim AI Agent v{}", env!("CARGO_PKG_VERSION"));
println!();
println!("Interactive Mode (requires TTY):");
println!(" terraphim-agent # Start REPL or TUI");
println!(" terraphim-agent # Start REPL (default)");
println!(" terraphim-agent --tui # Start TUI (requires server)");
println!(" terraphim-agent repl # Explicit REPL mode");
println!();
println!("Common Commands:");
Expand Down Expand Up @@ -392,6 +393,9 @@ struct Cli {
/// Output format (human, json, json-compact)
#[arg(long, value_enum, default_value_t = OutputFormat::Human)]
format: OutputFormat,
/// Force TUI mode (default is REPL)
#[arg(long, default_value_t = false)]
tui: bool,
#[command(subcommand)]
command: Option<Command>,
}
Expand Down Expand Up @@ -568,7 +572,8 @@ fn main() -> Result<()> {
});

match cli.command {
Some(Command::Interactive) | None => {
Some(Command::Interactive) => {
// Explicit interactive command - force TUI mode
// Check if we're in a TTY for interactive mode (both stdout and stdin required)
use atty::Stream;
if !atty::is(Stream::Stdout) {
Expand All @@ -589,6 +594,46 @@ fn main() -> Result<()> {
}
}

None => {
// No command specified - default to REPL mode or TUI mode if --tui flag is set
if cli.tui {
// Check if we're in a TTY for TUI mode
use atty::Stream;
if !atty::is(Stream::Stdout) {
eprintln!("Error: TUI mode requires a TTY (terminal)");
std::process::exit(1);
}

if !atty::is(Stream::Stdin) {
eprintln!("Error: TUI mode requires a TTY (terminal)");
std::process::exit(1);
}

if cli.server {
run_tui_server_mode(&cli.server_url, cli.transparent)
} else {
run_tui_offline_mode(cli.transparent)
}
} else {
// Default to REPL mode
#[cfg(feature = "repl")]
{
let rt = Runtime::new()?;
if cli.server {
rt.block_on(repl::run_repl_server_mode(&cli.server_url))
} else {
rt.block_on(repl::run_repl_offline_mode())
}
}

#[cfg(not(feature = "repl"))]
{
// If repl feature is not enabled, show error
anyhow::bail!("REPL mode requires 'repl' feature. Build with: cargo build --features repl");
}
}
}

#[cfg(feature = "repl")]
Some(Command::Repl { server, server_url }) => {
let rt = Runtime::new()?;
Expand Down
11 changes: 6 additions & 5 deletions crates/terraphim_agent/src/repl/mcp_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct McpToolsHandler {
}

#[cfg(feature = "repl-mcp")]
#[allow(dead_code)]
impl McpToolsHandler {
/// Create a new McpToolsHandler with a reference to the TuiService
pub fn new(service: Arc<TuiService>) -> Self {
Expand Down Expand Up @@ -52,10 +53,9 @@ impl McpToolsHandler {
exclude_term: bool,
) -> anyhow::Result<Vec<(String, String)>> {
let role = self.get_role().await;
Ok(self
.service
self.service
.extract_paragraphs(&role, text, exclude_term)
.await?)
.await
}

/// Find all thesaurus term matches in the given text
Expand All @@ -80,9 +80,10 @@ impl McpToolsHandler {
let role = self.get_role().await;
let link_type = match format.as_deref() {
Some("html") => LinkType::HTMLLinks,
Some("markdown") | _ => LinkType::MarkdownLinks,
Some("markdown") | None => LinkType::MarkdownLinks,
_ => LinkType::MarkdownLinks,
};
Ok(self.service.replace_matches(&role, text, link_type).await?)
self.service.replace_matches(&role, text, link_type).await
}

/// Get thesaurus entries for a role
Expand Down
73 changes: 0 additions & 73 deletions crates/terraphim_persistence/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,6 @@ pub async fn parse_profile(
}
#[cfg(feature = "services-redis")]
Scheme::Redis => Operator::from_iter::<services::Redis>(profile.clone())?.finish(),
#[cfg(feature = "services-rocksdb")]
Scheme::Rocksdb => Operator::from_iter::<services::Rocksdb>(profile.clone())?.finish(),
#[cfg(feature = "services-redb")]
Scheme::Redb => {
// Ensure parent directory exists for ReDB database file
Expand Down Expand Up @@ -468,77 +466,6 @@ mod tests {
Ok(())
}

/// Test saving and loading a struct to rocksdb profile
#[cfg(feature = "services-rocksdb")]
#[tokio::test]
#[serial_test::serial]
async fn test_save_and_load_rocksdb() -> Result<()> {
use tempfile::TempDir;

// Create temporary directory for test
let temp_dir = TempDir::new().unwrap();
let rocksdb_path = temp_dir.path().join("test_rocksdb");

// Create test settings with rocksdb profile
let mut profiles = std::collections::HashMap::new();

// DashMap profile (needed as fastest operator fallback)
let mut dashmap_profile = std::collections::HashMap::new();
dashmap_profile.insert("type".to_string(), "dashmap".to_string());
dashmap_profile.insert(
"root".to_string(),
temp_dir
.path()
.join("dashmap")
.to_string_lossy()
.to_string(),
);
profiles.insert("dashmap".to_string(), dashmap_profile);

// RocksDB profile for testing
let mut rocksdb_profile = std::collections::HashMap::new();
rocksdb_profile.insert("type".to_string(), "rocksdb".to_string());
rocksdb_profile.insert(
"datadir".to_string(),
rocksdb_path.to_string_lossy().to_string(),
);
profiles.insert("rocksdb".to_string(), rocksdb_profile);

let settings = DeviceSettings {
server_hostname: "localhost:8000".to_string(),
api_endpoint: "http://localhost:8000/api".to_string(),
initialized: false,
default_data_path: temp_dir.path().to_string_lossy().to_string(),
profiles,
};

// Initialize storage with custom settings
let storage = crate::init_device_storage_with_settings(settings).await?;

// Verify rocksdb profile is available
assert!(
storage.ops.contains_key("rocksdb"),
"RocksDB profile should be available. Available profiles: {:?}",
storage.ops.keys().collect::<Vec<_>>()
);

// Test direct operator write/read
let rocksdb_op = &storage.ops.get("rocksdb").unwrap().0;
let test_key = "test_rocksdb_key.json";
let test_data = r#"{"name":"Test RocksDB Object","age":30}"#;

rocksdb_op.write(test_key, test_data).await?;
let read_data = rocksdb_op.read(test_key).await?;
let read_str = String::from_utf8(read_data.to_vec()).unwrap();

assert_eq!(
test_data, read_str,
"RocksDB read data should match written data"
);

Ok(())
}

/// Test saving and loading a struct to dashmap profile (if available)
#[cfg(feature = "dashmap")]
#[tokio::test]
Expand Down
66 changes: 0 additions & 66 deletions crates/terraphim_persistence/src/thesaurus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,72 +91,6 @@ mod tests {
Ok(())
}

/// Test saving and loading a thesaurus to rocksdb profile
#[cfg(feature = "services-rocksdb")]
#[tokio::test]
#[serial_test::serial]
async fn test_save_and_load_thesaurus_rocksdb() -> Result<()> {
use tempfile::TempDir;
use terraphim_settings::DeviceSettings;

// Create temporary directory for test
let temp_dir = TempDir::new().unwrap();
let rocksdb_path = temp_dir.path().join("test_thesaurus_rocksdb");

// Create test settings with rocksdb profile
let mut profiles = std::collections::HashMap::new();

// Memory profile (needed as fastest operator fallback)
let mut memory_profile = std::collections::HashMap::new();
memory_profile.insert("type".to_string(), "memory".to_string());
profiles.insert("memory".to_string(), memory_profile);

// RocksDB profile for testing
let mut rocksdb_profile = std::collections::HashMap::new();
rocksdb_profile.insert("type".to_string(), "rocksdb".to_string());
rocksdb_profile.insert(
"datadir".to_string(),
rocksdb_path.to_string_lossy().to_string(),
);
profiles.insert("rocksdb".to_string(), rocksdb_profile);

let settings = DeviceSettings {
server_hostname: "localhost:8000".to_string(),
api_endpoint: "http://localhost:8000/api".to_string(),
initialized: false,
default_data_path: temp_dir.path().to_string_lossy().to_string(),
profiles,
};

// Initialize storage with custom settings
let storage = crate::init_device_storage_with_settings(settings).await?;

// Verify rocksdb profile is available
assert!(
storage.ops.contains_key("rocksdb"),
"RocksDB profile should be available. Available profiles: {:?}",
storage.ops.keys().collect::<Vec<_>>()
);

// Test direct operator write/read with thesaurus data
let rocksdb_op = &storage.ops.get("rocksdb").unwrap().0;
let test_key = "thesaurus_test_rocksdb_thesaurus.json";
let test_thesaurus = Thesaurus::new("Test RocksDB Thesaurus".to_string());
let test_data = serde_json::to_string(&test_thesaurus).unwrap();

rocksdb_op.write(test_key, test_data.clone()).await?;
let read_data = rocksdb_op.read(test_key).await?;
let read_str = String::from_utf8(read_data.to_vec()).unwrap();
let loaded_thesaurus: Thesaurus = serde_json::from_str(&read_str).unwrap();

assert_eq!(
test_thesaurus, loaded_thesaurus,
"Loaded RocksDB thesaurus does not match the original"
);

Ok(())
}

/// Test saving and loading a thesaurus to memory profile
#[tokio::test]
#[serial_test::serial]
Expand Down
8 changes: 4 additions & 4 deletions crates/terraphim_update/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl TerraphimUpdater {
builder.show_download_progress(show_progress);

// Set custom install path to preserve underscore naming
builder.bin_install_path(&format!("/usr/local/bin/{}", bin_name));
builder.bin_install_path(format!("/usr/local/bin/{}", bin_name));

match builder.build() {
Ok(updater) => {
Expand Down Expand Up @@ -285,7 +285,7 @@ impl TerraphimUpdater {
builder.verifying_keys(vec![key_array]); // Enable signature verification

// Set custom install path to preserve underscore naming
builder.bin_install_path(&format!("/usr/local/bin/{}", bin_name));
builder.bin_install_path(format!("/usr/local/bin/{}", bin_name));

match builder.build() {
Ok(updater) => match updater.update() {
Expand Down Expand Up @@ -540,7 +540,7 @@ impl TerraphimUpdater {
builder.current_version(current_version);

// Set custom install path to preserve underscore naming
builder.bin_install_path(&format!("/usr/local/bin/{}", bin_name));
builder.bin_install_path(format!("/usr/local/bin/{}", bin_name));

let updater = builder.build()?;

Expand Down Expand Up @@ -905,7 +905,7 @@ pub async fn check_for_updates_auto(bin_name: &str, current_version: &str) -> Re
builder.current_version(&current_version);

// Set custom install path to preserve underscore naming
builder.bin_install_path(&format!("/usr/local/bin/{}", bin_name));
builder.bin_install_path(format!("/usr/local/bin/{}", bin_name));

match builder.build() {
Ok(updater) => match updater.get_latest_release() {
Expand Down
Loading