Skip to content

Commit 4b522ec

Browse files
committed
Merge remote-tracking branch 'origin/main' into bskiser/move-rts-from-agent-to-chat
2 parents 0c4e418 + 846669f commit 4b522ec

File tree

24 files changed

+272
-63
lines changed

24 files changed

+272
-63
lines changed

crates/chat-cli/src/agent/rts/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ mod tests {
697697
Env,
698698
Fs,
699699
};
700-
use crate::util::is_integ_test;
700+
use crate::util::env_var::is_integ_test;
701701

702702
/// Manual test to verify cancellation succeeds in a timely manner.
703703
#[tokio::test]

crates/chat-cli/src/api_client/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::os::{
7070
Env,
7171
Fs,
7272
};
73-
use crate::util::is_integ_test;
73+
use crate::util::env_var::is_integ_test;
7474

7575
// Opt out constants
7676
pub const X_AMZN_CODEWHISPERER_OPT_OUT_HEADER: &str = "x-amzn-codewhisperer-optout";
@@ -144,7 +144,7 @@ impl ApiClient {
144144
model_cache: Arc::new(RwLock::new(None)),
145145
};
146146

147-
if let Ok(json) = env.get("Q_MOCK_CHAT_RESPONSE") {
147+
if let Some(json) = crate::util::env_var::get_mock_chat_response(env) {
148148
this.set_mock_output(serde_json::from_str(fs.read_to_string(json).await.unwrap().as_str()).unwrap());
149149
}
150150

@@ -154,7 +154,7 @@ impl ApiClient {
154154
// If SIGV4_AUTH_ENABLED is true, use Q developer client
155155
let mut streaming_client = None;
156156
let mut sigv4_streaming_client = None;
157-
match env.get("AMAZON_Q_SIGV4").is_ok() {
157+
match crate::util::env_var::is_sigv4_enabled(env) {
158158
true => {
159159
let credentials_chain = CredentialsChain::new().await;
160160
if let Err(err) = credentials_chain.provide_credentials().await {

crates/chat-cli/src/auth/builder_id.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ use crate::database::{
6363
Database,
6464
Secret,
6565
};
66-
use crate::util::is_integ_test;
66+
use crate::os::Env;
67+
use crate::util::env_var::{
68+
is_integ_test,
69+
is_sigv4_enabled,
70+
};
6771

6872
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6973
pub enum OAuthFlow {
@@ -590,7 +594,7 @@ pub async fn poll_create_token(
590594

591595
pub async fn is_logged_in(database: &mut Database) -> bool {
592596
// Check for BuilderId if not using Sigv4
593-
if std::env::var("AMAZON_Q_SIGV4").is_ok_and(|v| !v.is_empty()) {
597+
if is_sigv4_enabled(&Env::new()) {
594598
debug!("logged in using sigv4 credentials");
595599
return true;
596600
}

crates/chat-cli/src/cli/agent/mod.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ impl Agents {
563563
};
564564

565565
let mut agents = Vec::<Agent>::new();
566-
let results = load_agents_from_entries(files, os, &mut global_mcp_config, mcp_enabled, output).await;
566+
let results = load_agents_from_entries(files, os, &mut global_mcp_config, mcp_enabled, false, output).await;
567567
for result in results {
568568
match result {
569569
Ok(agent) => agents.push(agent),
@@ -601,7 +601,7 @@ impl Agents {
601601
};
602602

603603
let mut agents = Vec::<Agent>::new();
604-
let results = load_agents_from_entries(files, os, &mut global_mcp_config, mcp_enabled, output).await;
604+
let results = load_agents_from_entries(files, os, &mut global_mcp_config, mcp_enabled, true, output).await;
605605
for result in results {
606606
match result {
607607
Ok(agent) => agents.push(agent),
@@ -886,6 +886,7 @@ async fn load_agents_from_entries(
886886
os: &Os,
887887
global_mcp_config: &mut Option<McpServerConfig>,
888888
mcp_enabled: bool,
889+
is_from_global_dir: bool,
889890
output: &mut impl Write,
890891
) -> Vec<Result<Agent, AgentConfigError>> {
891892
let mut res = Vec::<Result<Agent, AgentConfigError>>::new();
@@ -897,7 +898,30 @@ async fn load_agents_from_entries(
897898
.and_then(OsStr::to_str)
898899
.is_some_and(|s| s == "json")
899900
{
900-
res.push(Agent::load(os, file_path, global_mcp_config, mcp_enabled, output).await);
901+
let agent_res = Agent::load(os, file_path, global_mcp_config, mcp_enabled, output).await;
902+
if let Ok(agent) = &agent_res {
903+
if res.iter().any(|res| match res {
904+
Ok(a) => a.name == agent.name,
905+
Err(_) => false,
906+
}) {
907+
let _ = queue!(
908+
output,
909+
StyledText::warning_fg(),
910+
style::Print("WARNING: "),
911+
StyledText::reset(),
912+
style::Print("Duplicate agent with name "),
913+
StyledText::success_fg(),
914+
style::Print(&agent.name),
915+
StyledText::reset(),
916+
style::Print(" was found in the "),
917+
style::Print(if is_from_global_dir { "global" } else { "workspace" }),
918+
style::Print(" directory.\n"),
919+
StyledText::reset(),
920+
);
921+
continue;
922+
}
923+
}
924+
res.push(agent_res);
901925
}
902926
}
903927

@@ -996,6 +1020,7 @@ fn validate_agent_name(name: &str) -> eyre::Result<()> {
9961020
mod tests {
9971021
use std::fs;
9981022

1023+
use bstr::ByteSlice;
9991024
use serde_json::json;
10001025
use tempfile::TempDir;
10011026

@@ -1581,4 +1606,44 @@ mod tests {
15811606
let result = agent.resolve_prompt();
15821607
assert!(result.is_err());
15831608
}
1609+
1610+
#[tokio::test]
1611+
async fn test_load_agents_from_entries_warns_duplicate() {
1612+
// Given two agents with the same name
1613+
let os = Os::new().await.unwrap();
1614+
let agents = [
1615+
Agent {
1616+
name: "test-agent".to_string(),
1617+
..Default::default()
1618+
},
1619+
Agent {
1620+
name: "test-agent".to_string(),
1621+
..Default::default()
1622+
},
1623+
];
1624+
for (i, agent) in agents.iter().enumerate() {
1625+
os.fs
1626+
.write(format!("{}_{}.json", agent.name, i), agent.to_str_pretty().unwrap())
1627+
.await
1628+
.unwrap();
1629+
}
1630+
1631+
// When we load them
1632+
let mut output = Vec::new();
1633+
let results = load_agents_from_entries(
1634+
os.fs.read_dir(".").await.unwrap(),
1635+
&os,
1636+
&mut None,
1637+
false,
1638+
false,
1639+
&mut output,
1640+
)
1641+
.await;
1642+
1643+
// We should see a warning
1644+
assert!(output.contains_str("WARNING"));
1645+
assert!(output.contains_str("test-agent"));
1646+
assert!(output.contains_str("workspace"));
1647+
assert_eq!(results.len(), 1);
1648+
}
15841649
}

crates/chat-cli/src/cli/chat/cli/editor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::cli::chat::{
1111
ChatState,
1212
};
1313
use crate::theme::StyledText;
14+
use crate::util::env_var::get_editor;
1415

1516
#[deny(missing_docs)]
1617
#[derive(Debug, PartialEq, Args)]
@@ -86,7 +87,7 @@ impl EditorArgs {
8687
/// Launch the user's preferred editor with the given file path
8788
fn launch_editor(file_path: &std::path::Path) -> Result<(), ChatError> {
8889
// Get the editor from environment variable or use a default
89-
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
90+
let editor_cmd = get_editor();
9091

9192
// Parse the editor command to handle arguments
9293
let mut parts =

crates/chat-cli/src/cli/chat/tools/delegate.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::cli::{
4141
};
4242
use crate::os::Os;
4343
use crate::theme::StyledText;
44+
use crate::util::env_var::get_all_env_vars;
4445
use crate::util::paths::PathResolver;
4546

4647
/// Launch and manage async agent processes. Delegate tasks to agents that run independently in
@@ -334,7 +335,7 @@ pub async fn spawn_agent_process(os: &Os, agent: &str, task: &str) -> Result<Age
334335
cmd.stdout(std::process::Stdio::piped());
335336
cmd.stderr(std::process::Stdio::piped());
336337
cmd.stdin(std::process::Stdio::null()); // No user input
337-
cmd.envs(std::env::vars());
338+
cmd.envs(get_all_env_vars());
338339

339340
#[cfg(not(windows))]
340341
cmd.process_group(0);

crates/chat-cli/src/cli/chat/tools/execute/unix.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use super::{
1616
format_output,
1717
};
1818
use crate::os::Os;
19+
use crate::util::env_var::get_chat_shell;
1920

2021
/// Run a bash command on Unix systems.
2122
/// # Arguments
@@ -30,7 +31,7 @@ pub async fn run_command<W: Write>(
3031
max_result_size: usize,
3132
mut updates: Option<W>,
3233
) -> Result<CommandResult> {
33-
let shell = std::env::var("AMAZON_Q_CHAT_SHELL").unwrap_or("bash".to_string());
34+
let shell = get_chat_shell();
3435

3536
// Set up environment variables with user agent metadata for CloudTrail tracking
3637
let env_vars = env_vars_with_user_agent(os);

crates/chat-cli/src/cli/chat/tools/fs_write.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,11 @@ impl FsWrite {
311311
let relative_path = format_path(cwd, &path);
312312
let prev = if os.fs.exists(&path) {
313313
let file = os.fs.read_to_string_sync(&path)?;
314-
stylize_output_if_able(os, &path, &file)
314+
stylize_output_if_able(&path, &file)
315315
} else {
316316
Default::default()
317317
};
318-
let new = stylize_output_if_able(os, &relative_path, &file_text);
318+
let new = stylize_output_if_able(&relative_path, &file_text);
319319
print_diff(output, &prev, &new, 1)?;
320320

321321
// Display summary as purpose if available after the diff
@@ -343,8 +343,8 @@ impl FsWrite {
343343
let old = [prefix, insert_line_content, suffix].join("");
344344
let new = [prefix, insert_line_content, new_str, suffix].join("");
345345

346-
let old = stylize_output_if_able(os, &relative_path, &old);
347-
let new = stylize_output_if_able(os, &relative_path, &new);
346+
let old = stylize_output_if_able(&relative_path, &old);
347+
let new = stylize_output_if_able(&relative_path, &new);
348348
print_diff(output, &old, &new, start_line)?;
349349

350350
// Display summary as purpose if available after the diff
@@ -362,8 +362,8 @@ impl FsWrite {
362362
Some((start_line, end_line)) => (start_line, end_line),
363363
_ => (0, 0),
364364
};
365-
let old_str = stylize_output_if_able(os, &relative_path, old_str);
366-
let new_str = stylize_output_if_able(os, &relative_path, new_str);
365+
let old_str = stylize_output_if_able(&relative_path, old_str);
366+
let new_str = stylize_output_if_able(&relative_path, new_str);
367367
print_diff(output, &old_str, &new_str, start_line)?;
368368

369369
// Display summary as purpose if available after the diff
@@ -375,7 +375,7 @@ impl FsWrite {
375375
let path = sanitize_path_tool_arg(os, path);
376376
let relative_path = format_path(cwd, &path);
377377
let start_line = os.fs.read_to_string_sync(&path)?.lines().count() + 1;
378-
let file = stylize_output_if_able(os, &relative_path, new_str);
378+
let file = stylize_output_if_able(&relative_path, new_str);
379379
print_diff(output, &Default::default(), &file, start_line)?;
380380

381381
// Display summary as purpose if available after the diff
@@ -763,8 +763,8 @@ fn terminal_width_required_for_line_count(line_count: usize) -> usize {
763763
line_count.to_string().chars().count()
764764
}
765765

766-
fn stylize_output_if_able(os: &Os, path: impl AsRef<Path>, file_text: &str) -> StylizedFile {
767-
if supports_truecolor(os) {
766+
fn stylize_output_if_able(path: impl AsRef<Path>, file_text: &str) -> StylizedFile {
767+
if supports_truecolor() {
768768
match stylized_file(path, file_text) {
769769
Ok(s) => return s,
770770
Err(err) => {

crates/chat-cli/src/cli/chat/tools/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,9 @@ fn format_path(cwd: impl AsRef<Path>, path: impl AsRef<Path>) -> String {
444444
.unwrap_or(path.as_ref().to_string_lossy().to_string())
445445
}
446446

447-
fn supports_truecolor(os: &Os) -> bool {
447+
fn supports_truecolor() -> bool {
448448
// Simple override to disable truecolor since shell_color doesn't use Context.
449-
!os.env.get("Q_DISABLE_TRUECOLOR").is_ok_and(|s| !s.is_empty())
449+
!crate::util::env_var::is_truecolor_disabled()
450450
&& shell_color::get_color_support().contains(shell_color::ColorSupport::TERM24BIT)
451451
}
452452

@@ -514,7 +514,7 @@ pub fn queue_function_result(result: &str, updates: &mut impl Write, is_error: b
514514

515515
/// Helper function to set up environment variables with user agent metadata for CloudTrail tracking
516516
pub fn env_vars_with_user_agent(os: &Os) -> std::collections::HashMap<String, String> {
517-
let mut env_vars: std::collections::HashMap<String, String> = std::env::vars().collect();
517+
let mut env_vars: std::collections::HashMap<String, String> = crate::util::env_var::get_all_env_vars().collect();
518518

519519
// Set up additional metadata for the AWS CLI user agent
520520
let user_agent_metadata_value = format!(

crates/chat-cli/src/cli/chat/util/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use eyre::Result;
1616

1717
use super::ChatError;
1818
use super::token_counter::TokenCounter;
19+
use crate::util::env_var::get_term;
1920

2021
pub fn truncate_safe(s: &str, max_bytes: usize) -> &str {
2122
if s.len() <= max_bytes {
@@ -119,7 +120,7 @@ pub fn play_notification_bell(requires_confirmation: bool) {
119120
/// Determine if we should play the bell based on terminal type
120121
fn should_play_bell() -> bool {
121122
// Get the TERM environment variable
122-
if let Ok(term) = std::env::var("TERM") {
123+
if let Some(term) = get_term() {
123124
// List of terminals known to handle bell character well
124125
let bell_compatible_terms = [
125126
"xterm",

0 commit comments

Comments
 (0)