Skip to content
Merged
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
43 changes: 43 additions & 0 deletions core/src/agent/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,46 @@ Read the skill's SKILL.md file using the `read_file` tool to learn how to use it
&self.skills
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::config::default_config;
use tempfile::tempdir;

#[tokio::test]
async fn test_skills_summary_includes_pr_review() {
let temp = tempdir().expect("tempdir");
let mut config = default_config();
config.agents.defaults.workspace = temp.path().display().to_string();

let context = ContextBuilder::new(&config);
let prompt = context
.build_system_prompt(None)
.await
.expect("system prompt");

assert!(
prompt.contains("pr-review"),
"prompt did not include pr-review skill summary"
);
Comment on lines +341 to +356
}

#[tokio::test]
async fn test_requested_pr_review_skill_is_loaded() {
let temp = tempdir().expect("tempdir");
let mut config = default_config();
config.agents.defaults.workspace = temp.path().display().to_string();

let context = ContextBuilder::new(&config);
let requested = vec!["pr-review".to_string()];
let prompt = context
.build_system_prompt(Some(&requested))
.await
.expect("system prompt");

assert!(prompt.contains("# Requested Skills"));
assert!(prompt.contains("# PR Review Skill"));
assert!(prompt.contains("PR Template Compliance"));
}
}
22 changes: 16 additions & 6 deletions core/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ impl SessionManager {

/// Get an existing session or create a new one (delegates to mofa)
pub async fn get_or_create(&self, key: impl Into<String>) -> Session {
self.inner.get_or_create(&key.into()).await
let key = key.into();
self.inner.get_or_create(&storage_key(&key)).await
}

/// Load a session from disk
Expand Down Expand Up @@ -127,7 +128,7 @@ impl SessionManager {
/// Delete a session (delegates to mofa)
pub async fn delete(&self, key: &str) -> Result<bool> {
self.inner
.delete(key)
.delete(&storage_key(key))
.await
.map_err(|e| crate::error::SessionError::LoadFailed(e.to_string()).into())
}
Expand Down Expand Up @@ -177,8 +178,7 @@ impl SessionManager {

/// Get the file path for a session
fn get_session_path(&self, key: &str) -> PathBuf {
let safe_key = safe_filename(&key.replace(':', "_"));
self.sessions_dir.join(format!("{}.jsonl", safe_key))
self.sessions_dir.join(format!("{}.jsonl", storage_key(key)))
}

/// Get the inner mofa SessionManager (for advanced usage)
Expand All @@ -200,6 +200,10 @@ fn safe_filename(name: &str) -> String {
.collect()
}

fn storage_key(key: &str) -> String {
safe_filename(&key.replace(':', "_"))
}

// ============================================================================
// Conversion utilities between mofaclaw Message and mofa SessionMessage
// ============================================================================
Expand Down Expand Up @@ -305,6 +309,12 @@ mod tests {
assert_eq!(safe_filename("test/file@name"), "test_file_name");
}

#[test]
fn test_storage_key_replaces_windows_unsafe_separators() {
assert_eq!(storage_key("cli:default"), "cli_default");
assert_eq!(storage_key("discord:general/chat"), "discord_general_chat");
}

#[test]
fn test_message_conversion() {
// Test Message -> SessionMessage
Expand Down Expand Up @@ -354,13 +364,13 @@ mod tests {
let manager = SessionManager::with_sessions_dir(temp_dir.path().to_path_buf());

let session = manager.get_or_create("test:session").await;
assert_eq!(session.key, "test:session");
assert_eq!(session.key, "test_session");

manager.save(&session).await.unwrap();

// Reload
let loaded = manager.get_or_create("test:session").await;
assert_eq!(loaded.key, "test:session");
assert_eq!(loaded.key, "test_session");
}

#[tokio::test]
Expand Down
1 change: 1 addition & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This directory contains the builtin skills for mofaclaw. These skills are automa

- **weather** - Get current weather and forecasts using wttr.in (no API key required)
- **github** - Interact with GitHub using the `gh` CLI
- **pr-review** - Review pull requests with structured checks for scope, security, tests, CI, and reviewer comments
- **summarize** - Summarize or extract text/transcripts from URLs, podcasts, and local files
- **tmux** - Remote-control tmux sessions for interactive CLIs
- **skill-creator** - Meta-skill for creating new skills
Expand Down
Loading
Loading