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
31 changes: 18 additions & 13 deletions crates/chat-cli/src/cli/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,15 @@ impl Default for Agent {
set.extend(default_approve);
set
},
resources: vec!["file://AmazonQ.md", "file://AGENTS.md", "file://README.md", "file://.amazonq/rules/**/*.md"]
.into_iter()
.map(Into::into)
.collect::<Vec<_>>(),
resources: vec![
"file://AmazonQ.md",
"file://AGENTS.md",
"file://README.md",
"file://.amazonq/rules/**/*.md",
]
.into_iter()
.map(Into::into)
.collect::<Vec<_>>(),
hooks: Default::default(),
tools_settings: Default::default(),
use_legacy_mcp_json: true,
Expand Down Expand Up @@ -1298,14 +1303,14 @@ mod tests {
"name": "test-agent",
"model": "claude-sonnet-4"
}"#;

let agent: Agent = serde_json::from_str(agent_json).expect("Failed to deserialize agent with model");
assert_eq!(agent.model, Some("claude-sonnet-4".to_string()));

// Test default agent has no model
let default_agent = Agent::default();
assert_eq!(default_agent.model, None);

// Test serialization includes model field
let agent_with_model = Agent {
model: Some("test-model".to_string()),
Expand All @@ -1319,33 +1324,33 @@ mod tests {
fn test_agent_model_fallback_priority() {
// Test that agent model is checked and falls back correctly
let mut agents = Agents::default();

// Create agent with unavailable model
let agent_with_invalid_model = Agent {
name: "test-agent".to_string(),
model: Some("unavailable-model".to_string()),
..Default::default()
};

agents.agents.insert("test-agent".to_string(), agent_with_invalid_model);
agents.active_idx = "test-agent".to_string();

// Verify the agent has the model set
assert_eq!(
agents.get_active().and_then(|a| a.model.as_ref()),
Some(&"unavailable-model".to_string())
);

// Test agent without model
let agent_without_model = Agent {
name: "no-model-agent".to_string(),
model: None,
..Default::default()
};

agents.agents.insert("no-model-agent".to_string(), agent_without_model);
agents.active_idx = "no-model-agent".to_string();

assert_eq!(agents.get_active().and_then(|a| a.model.as_ref()), None);
}
}
43 changes: 40 additions & 3 deletions crates/chat-cli/src/cli/chat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ use clap::{
Args,
CommandFactory,
Parser,
ValueEnum,
};
use cli::compact::CompactStrategy;
use cli::model::{
find_model,
get_available_models,
select_model,
find_model,
};
pub use conversation::ConversationState;
use conversation::TokenWarningLevel;
Expand Down Expand Up @@ -189,6 +190,16 @@ pub const EXTRA_HELP: &str = color_print::cstr! {"
<black!>Change using: q settings chat.skimCommandKey x</black!>
"};

#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
pub enum WrapMode {
/// Always wrap at terminal width
Always,
/// Never wrap (raw output)
Never,
/// Auto-detect based on output target (default)
Auto,
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Args)]
pub struct ChatArgs {
/// Resumes the previous conversation from this directory.
Expand All @@ -212,6 +223,9 @@ pub struct ChatArgs {
pub no_interactive: bool,
/// The first question to ask
pub input: Option<String>,
/// Control line wrapping behavior (default: auto-detect)
#[arg(short = 'w', long, value_enum)]
pub wrap: Option<WrapMode>,
}

impl ChatArgs {
Expand Down Expand Up @@ -343,7 +357,9 @@ impl ChatArgs {
// Fallback logic: try user's saved default, then system default
let fallback_model_id = || {
if let Some(saved) = os.database.settings.get_string(Setting::ChatDefaultModel) {
find_model(&models, &saved).map(|m| m.model_id.clone()).or(Some(default_model_opt.model_id.clone()))
find_model(&models, &saved)
.map(|m| m.model_id.clone())
.or(Some(default_model_opt.model_id.clone()))
} else {
Some(default_model_opt.model_id.clone())
}
Expand Down Expand Up @@ -412,6 +428,7 @@ impl ChatArgs {
tool_config,
!self.no_interactive,
mcp_enabled,
self.wrap,
)
.await?
.spawn(os)
Expand Down Expand Up @@ -621,6 +638,7 @@ pub struct ChatSession {
interactive: bool,
inner: Option<ChatState>,
ctrlc_rx: broadcast::Receiver<()>,
wrap: Option<WrapMode>,
}

impl ChatSession {
Expand All @@ -640,6 +658,7 @@ impl ChatSession {
tool_config: HashMap<String, ToolSpec>,
interactive: bool,
mcp_enabled: bool,
wrap: Option<WrapMode>,
) -> Result<Self> {
// Reload prior conversation
let mut existing_conversation = false;
Expand Down Expand Up @@ -731,6 +750,7 @@ impl ChatSession {
interactive,
inner: Some(ChatState::default()),
ctrlc_rx,
wrap,
})
}

Expand Down Expand Up @@ -2419,8 +2439,20 @@ impl ChatSession {
let mut buf = String::new();
let mut offset = 0;
let mut ended = false;
let terminal_width = match self.wrap {
Some(WrapMode::Never) => None,
Some(WrapMode::Always) => Some(self.terminal_width()),
Some(WrapMode::Auto) | None => {
if std::io::stdout().is_terminal() {
Some(self.terminal_width())
} else {
None
}
},
};

let mut state = ParseState::new(
Some(self.terminal_width()),
terminal_width,
os.database.settings.get_bool(Setting::ChatDisableMarkdownRendering),
);
let mut response_prefix_printed = false;
Expand Down Expand Up @@ -3340,6 +3372,7 @@ mod tests {
tool_config,
true,
false,
None,
)
.await
.unwrap()
Expand Down Expand Up @@ -3482,6 +3515,7 @@ mod tests {
tool_config,
true,
false,
None,
)
.await
.unwrap()
Expand Down Expand Up @@ -3579,6 +3613,7 @@ mod tests {
tool_config,
true,
false,
None,
)
.await
.unwrap()
Expand Down Expand Up @@ -3654,6 +3689,7 @@ mod tests {
tool_config,
true,
false,
None,
)
.await
.unwrap()
Expand Down Expand Up @@ -3705,6 +3741,7 @@ mod tests {
tool_config,
true,
false,
None,
)
.await
.unwrap()
Expand Down
58 changes: 58 additions & 0 deletions crates/chat-cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ impl Cli {

#[cfg(test)]
mod test {
use chat::WrapMode::{
Always,
Auto,
Never,
};

use super::*;
use crate::util::CHAT_BINARY_NAME;
use crate::util::test::assert_parse;
Expand Down Expand Up @@ -370,6 +376,7 @@ mod test {
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: None,
})),
verbose: 2,
help_all: false,
Expand Down Expand Up @@ -409,6 +416,7 @@ mod test {
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: None,
})
);
}
Expand All @@ -425,6 +433,7 @@ mod test {
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: None,
})
);
}
Expand All @@ -441,6 +450,7 @@ mod test {
trust_all_tools: true,
trust_tools: None,
no_interactive: false,
wrap: None,
})
);
}
Expand All @@ -457,6 +467,7 @@ mod test {
trust_all_tools: false,
trust_tools: None,
no_interactive: true,
wrap: None,
})
);
assert_parse!(
Expand All @@ -469,6 +480,7 @@ mod test {
trust_all_tools: false,
trust_tools: None,
no_interactive: true,
wrap: None,
})
);
}
Expand All @@ -485,6 +497,7 @@ mod test {
trust_all_tools: true,
trust_tools: None,
no_interactive: false,
wrap: None,
})
);
}
Expand All @@ -501,6 +514,7 @@ mod test {
trust_all_tools: false,
trust_tools: Some(vec!["".to_string()]),
no_interactive: false,
wrap: None,
})
);
}
Expand All @@ -517,6 +531,50 @@ mod test {
trust_all_tools: false,
trust_tools: Some(vec!["fs_read".to_string(), "fs_write".to_string()]),
no_interactive: false,
wrap: None,
})
);
}

#[test]
fn test_chat_with_different_wrap_modes() {
assert_parse!(
["chat", "-w", "never"],
RootSubcommand::Chat(ChatArgs {
resume: false,
input: None,
agent: None,
model: None,
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: Some(Never),
})
);
assert_parse!(
["chat", "--wrap", "always"],
RootSubcommand::Chat(ChatArgs {
resume: false,
input: None,
agent: None,
model: None,
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: Some(Always),
})
);
assert_parse!(
["chat", "--wrap", "auto"],
RootSubcommand::Chat(ChatArgs {
resume: false,
input: None,
agent: None,
model: None,
trust_all_tools: false,
trust_tools: None,
no_interactive: false,
wrap: Some(Auto),
})
);
}
Expand Down
Loading