Refactor API integration and enhance command handling

- Removed unused modules and updated module paths for clarity.
- Added autocomplete functionality for command input in InputState.
- Updated AppStateRest to include a method for checking if a turn is in flight.
- Refactored subagent engine to use new API client structure.
- Changed default provider from "openrouter" to "zen" with updated API keys and models.
- Implemented tests for memory management and edit log functionalities.
- Enhanced error handling in API requests and improved response parsing.
- Updated UI components to reflect new API provider and status indicators.
This commit is contained in:
asepharyana
2026-07-11 22:10:17 +07:00
parent f6389018f5
commit 3dee2a1427
30 changed files with 788 additions and 222 deletions
+170 -1
View File
@@ -74,7 +74,8 @@ impl Memory {
}
pub fn parse(content: &str) -> std::io::Result<Self> {
let parts: Vec<&str> = content.splitn(2, "---\n").collect();
let content = content.strip_prefix("---\n").unwrap_or(content);
let parts: Vec<&str> = content.splitn(2, "\n---\n").collect();
if parts.len() < 2 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "missing frontmatter"));
}
@@ -218,6 +219,174 @@ pub fn auto_create_retrospective(session_dir: &Path, session: &Session) -> std::
Ok(Some(retrospective))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slugify_empty() {
assert_eq!(Memory::slugify(""), None);
}
#[test]
fn test_slugify_basic() {
assert_eq!(Memory::slugify("Hello World"), Some("hello-world".to_string()));
}
#[test]
fn test_slugify_special_chars() {
assert_eq!(Memory::slugify("Use & Avoid! @#$"), Some("use-avoid".to_string()));
}
#[test]
fn test_slugify_too_long() {
let long = "a".repeat(100);
assert_eq!(Memory::slugify(&long), None);
}
#[test]
fn test_slugify_numeric() {
assert_eq!(Memory::slugify("123"), Some("123".to_string()));
}
#[test]
fn test_memory_parse_basic() {
let md = "---\nname: test-memory\ndescription: A test memory\nkind: lesson\ncreated_at: 1000\nupdated_at: 2000\n---\n\nThis is the body.";
let mem = Memory::parse(md).unwrap();
assert_eq!(mem.name, "test-memory");
assert_eq!(mem.description, "A test memory");
assert_eq!(mem.kind, "lesson");
assert_eq!(mem.created_at, 1000);
assert_eq!(mem.updated_at, 2000);
assert_eq!(mem.content, "This is the body.");
}
#[test]
fn test_memory_parse_with_optional_fields() {
let md = "---\nname: full-memory\ndescription: Full fields\ntype: reference\ncreated_at: 100\nupdated_at: 200\nlifecycle: active\nscope: project\n---\n\nBody content here.";
let mem = Memory::parse(md).unwrap();
assert_eq!(mem.name, "full-memory");
assert_eq!(mem.lifecycle, "active");
assert_eq!(mem.scope, Some("project".to_string()));
assert_eq!(mem.content, "Body content here.");
}
#[test]
fn test_memory_parse_missing_frontmatter() {
let md = "No frontmatter here";
assert!(Memory::parse(md).is_err());
}
#[test]
fn test_memory_write_and_read() {
let dir = std::env::temp_dir().join("memory_test_write_read");
let _ = std::fs::create_dir_all(&dir);
let mem = Memory {
name: "my-test".to_string(),
description: "Test".to_string(),
content: "Some content".to_string(),
kind: "reference".to_string(),
created_at: 42,
updated_at: 43,
outcome: None,
lifecycle: "new".to_string(),
scope: None,
before_snippet: None,
after_snippet: None,
provenances: vec![],
};
mem.write(&dir).unwrap();
let read = Memory::read(&dir, "my-test").unwrap();
assert_eq!(read.name, "my-test");
assert_eq!(read.content, "Some content");
assert_eq!(read.created_at, 42);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn test_memory_list() {
let dir = std::env::temp_dir().join("memory_test_list");
let _ = std::fs::create_dir_all(&dir);
let mem = Memory {
name: "alpha".to_string(),
description: "A".to_string(),
content: "a".to_string(),
kind: "lesson".to_string(),
created_at: 1,
updated_at: 1,
outcome: None,
lifecycle: "new".to_string(),
scope: None,
before_snippet: None,
after_snippet: None,
provenances: vec![],
};
mem.write(&dir).unwrap();
let names = Memory::list(&dir);
assert!(names.contains(&"alpha".to_string()), "list should contain 'alpha', got: {:?}", names);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn test_memory_remove() {
let dir = std::env::temp_dir().join("memory_test_remove");
let _ = std::fs::create_dir_all(&dir);
let mem = Memory {
name: "remove-me".to_string(),
description: "R".to_string(),
content: "r".to_string(),
kind: "lesson".to_string(),
created_at: 1,
updated_at: 1,
outcome: None,
lifecycle: "new".to_string(),
scope: None,
before_snippet: None,
after_snippet: None,
provenances: vec![],
};
mem.write(&dir).unwrap();
assert!(Memory::read(&dir, "remove-me").is_ok());
Memory::remove(&dir, "remove-me").unwrap();
assert!(Memory::read(&dir, "remove-me").is_err());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn test_export_import_lessons() {
let dir = std::env::temp_dir().join("memory_test_export");
let _ = std::fs::create_dir_all(&dir);
let mem = Memory {
name: "export-me".to_string(),
description: "Exported".to_string(),
content: "content".to_string(),
kind: "lesson".to_string(),
created_at: 10,
updated_at: 10,
outcome: None,
lifecycle: "active".to_string(),
scope: Some("project".to_string()),
before_snippet: None,
after_snippet: None,
provenances: vec![],
};
mem.write(&dir).unwrap();
let export_path = std::env::temp_dir().join("memory_test_export_lessons.json");
export_lessons(&dir, &export_path).unwrap();
assert!(export_path.exists());
let dest_dir = std::env::temp_dir().join("memory_test_import_dest");
let _ = std::fs::create_dir_all(&dest_dir);
let imported = import_lessons(&dest_dir, &export_path).unwrap();
assert_eq!(imported, 1);
assert!(Memory::read(&dest_dir, "export-me").is_ok());
let _ = std::fs::remove_dir_all(&dir);
let _ = std::fs::remove_dir_all(&dest_dir);
let _ = std::fs::remove_file(&export_path);
}
}
pub fn create_retrospective(session_dir: &Path, session: &Session, lessons: &[Memory]) -> std::io::Result<Memory> {
let now = chrono::Utc::now().timestamp_millis();
let lessons_content: String = lessons.iter()