Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion crates/forge_display/src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl MarkdownFormat {
/// Create a new MarkdownFormat with the default skin
pub fn new() -> Self {
let mut skin = MadSkin::default();
let compound_style = CompoundStyle::new(Some(Color::Cyan), None, Attribute::Bold.into());
let compound_style = CompoundStyle::new(Some(Color::Cyan), None, Default::default());
skin.inline_code = compound_style.clone();

let codeblock_style = CompoundStyle::new(None, None, Default::default());
Expand Down
121 changes: 121 additions & 0 deletions crates/forge_main/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ pub struct Cli {
#[arg(long)]
pub conversation: Option<PathBuf>,

/// Conversation ID to use for this session.
///
/// If provided, the application will use this conversation ID instead of
/// generating a new one. This allows resuming or continuing existing
/// conversations.
#[arg(long, alias = "cid")]
pub conversation_id: Option<String>,

/// Agent ID to use for this session.
///
/// If provided, the application will use this agent for the conversation.
/// This allows specifying the agent from the command line.
#[arg(long, alias = "aid")]
pub agent_id: Option<String>,

/// Working directory to set before starting forge.
///
/// If provided, the application will change to this directory before
Expand Down Expand Up @@ -363,6 +378,22 @@ pub enum SessionCommand {
/// Conversation ID
id: String,
},

/// Show the last assistant message from a conversation
///
/// Example: forge session show abc123
Show {
/// Conversation ID
id: String,
},

/// Show detailed information about a session
///
/// Example: forge session info abc123
Info {
/// Conversation ID
id: String,
},
}

#[cfg(test)]
Expand Down Expand Up @@ -594,6 +625,19 @@ mod tests {
assert_eq!(id, "abc123");
}

#[test]
fn test_session_last_with_id() {
let fixture = Cli::parse_from(["forge", "session", "show", "test123"]);
let id = match fixture.subcommands {
Some(TopLevelCommand::Session(session)) => match session.command {
SessionCommand::Show { id } => id,
_ => String::new(),
},
_ => String::new(),
};
assert_eq!(id, "test123");
}

#[test]
fn test_session_resume() {
let fixture = Cli::parse_from(["forge", "session", "resume", "def456"]);
Expand Down Expand Up @@ -718,4 +762,81 @@ mod tests {
let expected = true;
assert_eq!(actual, expected);
}

#[test]
fn test_session_info_with_id() {
let fixture = Cli::parse_from(["forge", "session", "info", "abc123"]);
let id = match fixture.subcommands {
Some(TopLevelCommand::Session(session)) => match session.command {
SessionCommand::Info { id } => id,
_ => String::new(),
},
_ => String::new(),
};
assert_eq!(id, "abc123");
}

#[test]
fn test_session_info_with_porcelain() {
let fixture = Cli::parse_from(["forge", "session", "info", "test123", "--porcelain"]);
let (id, porcelain) = match fixture.subcommands {
Some(TopLevelCommand::Session(session)) => match session.command {
SessionCommand::Info { id } => (id, session.porcelain),
_ => (String::new(), false),
},
_ => (String::new(), false),
};
assert_eq!(id, "test123");
assert_eq!(porcelain, true);
}

#[test]
fn test_prompt_with_conversation_id() {
let fixture = Cli::parse_from([
"forge",
"-p",
"hello",
"--conversation-id",
"550e8400-e29b-41d4-a716-446655440000",
]);
let actual = fixture.conversation_id;
let expected = Some("550e8400-e29b-41d4-a716-446655440000".to_string());
assert_eq!(actual, expected);
}

#[test]
fn test_conversation_id_without_prompt() {
let fixture = Cli::parse_from([
"forge",
"--conversation-id",
"550e8400-e29b-41d4-a716-446655440000",
]);
let actual = fixture.conversation_id;
let expected = Some("550e8400-e29b-41d4-a716-446655440000".to_string());
assert_eq!(actual, expected);
}

#[test]
fn test_agent_id_with_prompt() {
let fixture = Cli::parse_from(["forge", "-p", "hello", "--agent-id", "sage"]);
let actual = fixture.agent_id;
let expected = Some("sage".to_string());
assert_eq!(actual, expected);
}

#[test]
fn test_agent_id_with_aid_alias() {
let fixture = Cli::parse_from(["forge", "-p", "hello", "--aid", "muse"]);
let actual = fixture.agent_id;
let expected = Some("muse".to_string());
assert_eq!(actual, expected);
}

#[test]
fn test_agent_id_without_prompt() {
let fixture = Cli::parse_from(["forge", "--agent-id", "forge"]);
let actual = fixture.agent_id;
let expected = Some("forge".to_string());
assert_eq!(actual, expected);
}
}
98 changes: 0 additions & 98 deletions crates/forge_main/src/env.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
use forge_api::{AgentId, ConversationId};

// Environment variable names
pub const FORGE_CONVERSATION_ID: &str = "FORGE_CONVERSATION_ID";
pub const FORGE_ACTIVE_AGENT: &str = "FORGE_ACTIVE_AGENT";
pub const FORGE_SHOW_TASK_STATS: &str = "FORGE_SHOW_TASK_STATS";

/// Get conversation ID from FORGE_CONVERSATION_ID environment variable
pub fn get_conversation_id_from_env() -> Option<ConversationId> {
std::env::var(FORGE_CONVERSATION_ID)
.ok()
.and_then(|env_id| forge_domain::ConversationId::parse(&env_id).ok())
}

/// Get agent ID from FORGE_ACTIVE_AGENT environment variable
pub fn get_agent_from_env() -> Option<AgentId> {
std::env::var(FORGE_ACTIVE_AGENT).ok().map(AgentId::new)
}

/// Check if the completion prompt should be shown
///
/// Returns true if the environment variable is not set, cannot be parsed, or is
Expand All @@ -28,85 +12,3 @@ pub fn should_show_completion_prompt() -> bool {
.and_then(|val| val.trim().parse::<bool>().ok())
.unwrap_or(true)
}

#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use serial_test::serial;

use super::*;

#[test]
fn test_get_conversation_id_from_env_with_valid_id() {
let fixture_env_value = "01234567-89ab-cdef-0123-456789abcdef";
unsafe {
std::env::set_var(FORGE_CONVERSATION_ID, fixture_env_value);
}

let actual = get_conversation_id_from_env();
let expected = forge_domain::ConversationId::parse(fixture_env_value).ok();

assert_eq!(actual, expected);
unsafe {
std::env::remove_var(FORGE_CONVERSATION_ID);
}
}

#[test]
fn test_get_conversation_id_from_env_with_invalid_id() {
let fixture_env_value = "invalid-uuid";
unsafe {
std::env::set_var(FORGE_CONVERSATION_ID, fixture_env_value);
}

let actual = get_conversation_id_from_env();
let expected = None;

assert_eq!(actual, expected);
unsafe {
std::env::remove_var(FORGE_CONVERSATION_ID);
}
}

#[test]
fn test_get_conversation_id_from_env_not_set() {
unsafe {
std::env::remove_var(FORGE_CONVERSATION_ID);
}

let actual = get_conversation_id_from_env();
let expected = None;

assert_eq!(actual, expected);
}

#[test]
#[serial]
fn test_get_agent_from_env_with_value() {
let fixture_env_value = "sage";
unsafe {
std::env::set_var(FORGE_ACTIVE_AGENT, fixture_env_value);
}

let actual = get_agent_from_env();
let expected = Some(AgentId::new("sage"));

assert_eq!(actual, expected);
unsafe {
std::env::remove_var(FORGE_ACTIVE_AGENT);
}
}

#[test]
#[serial]
fn test_get_agent_from_env_not_set() {
unsafe {
std::env::remove_var(FORGE_ACTIVE_AGENT);
}

let actual = get_agent_from_env();
let expected = None;

assert_eq!(actual, expected);
}
}
4 changes: 2 additions & 2 deletions crates/forge_main/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,12 @@ impl fmt::Display for Info {
writeln!(
f,
" {} {}",
format!("{key:<width$}:").bright_cyan().bold(),
format!("{key:<width$}:").yellow().bold(),
value
)?;
} else {
// No section width (items without a title)
writeln!(f, " {}: {}", key.bright_cyan().bold(), value)?;
writeln!(f, " {}: {}", key.yellow().bold(), value)?;
}
} else {
// Show value-only items
Expand Down
14 changes: 7 additions & 7 deletions crates/forge_main/src/porcelain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl fmt::Display for Porcelain {
} else {
// Pad to column width
line.push_str(&format!("{:<width$}", content, width = col_widths[i]));
line.push(' ');
line.push_str(" ");
}
}
lines.push(line);
Expand Down Expand Up @@ -536,9 +536,9 @@ mod tests {
let actual = info.to_string();
let expected = [
//
"$ID name age",
"user1 Alice 30",
"user2 Bob 25",
"$ID name age",
"user1 Alice 30",
"user2 Bob 25",
]
.join("\n");

Expand All @@ -560,9 +560,9 @@ mod tests {
let actual = info.to_string();
let expected = [
//
"$ID name age",
"user1 Alice 30",
"user2 25",
"$ID name age",
"user1 Alice 30",
"user2 25",
]
.join("\n");

Expand Down
Loading
Loading