Skip to content

Commit 3b213e8

Browse files
authored
feat: add auto-announcement feature with /changelog command (#2833)
1 parent c36892a commit 3b213e8

File tree

12 files changed

+544
-2
lines changed

12 files changed

+544
-2
lines changed

crates/chat-cli/build.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ fn write_plist() {
8383
fn main() {
8484
println!("cargo:rerun-if-changed=def.json");
8585

86+
// Download feed.json if FETCH_FEED environment variable is set
87+
if std::env::var("FETCH_FEED").is_ok() {
88+
download_feed_json();
89+
}
90+
8691
#[cfg(target_os = "macos")]
8792
write_plist();
8893

@@ -322,3 +327,58 @@ fn main() {
322327
// write an empty file to the output directory
323328
std::fs::write(format!("{}/mod.rs", outdir), pp).unwrap();
324329
}
330+
331+
/// Downloads the latest feed.json from the autocomplete repository.
332+
/// This ensures official builds have the most up-to-date changelog information.
333+
///
334+
/// # Errors
335+
///
336+
/// Prints cargo warnings if:
337+
/// - `curl` command is not available
338+
/// - Network request fails
339+
/// - File write operation fails
340+
fn download_feed_json() {
341+
use std::process::Command;
342+
343+
println!("cargo:warning=Downloading latest feed.json from autocomplete repo...");
344+
345+
// Check if curl is available first
346+
let curl_check = Command::new("curl").arg("--version").output();
347+
348+
if curl_check.is_err() {
349+
panic!(
350+
"curl command not found. Cannot download latest feed.json. Please install curl or build without FETCH_FEED=1 to use existing feed.json."
351+
);
352+
}
353+
354+
let output = Command::new("curl")
355+
.args([
356+
"-H",
357+
"Accept: application/vnd.github.v3.raw",
358+
"-s", // silent
359+
"-f", // fail on HTTP errors
360+
"https://api.github.com/repos/aws/amazon-q-developer-cli-autocomplete/contents/feed.json",
361+
])
362+
.output();
363+
364+
match output {
365+
Ok(result) if result.status.success() => {
366+
if let Err(e) = std::fs::write("src/cli/feed.json", result.stdout) {
367+
panic!("Failed to write feed.json: {}", e);
368+
} else {
369+
println!("cargo:warning=Successfully downloaded latest feed.json");
370+
}
371+
},
372+
Ok(result) => {
373+
let error_msg = if !result.stderr.is_empty() {
374+
format!("HTTP error: {}", String::from_utf8_lossy(&result.stderr))
375+
} else {
376+
"HTTP error occurred".to_string()
377+
};
378+
panic!("Failed to download feed.json: {}", error_msg);
379+
},
380+
Err(e) => {
381+
panic!("Failed to execute curl: {}", e);
382+
},
383+
}
384+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use clap::Args;
2+
use eyre::Result;
3+
4+
use crate::cli::chat::{
5+
ChatError,
6+
ChatSession,
7+
ChatState,
8+
};
9+
use crate::util::ui;
10+
11+
#[derive(Debug, PartialEq, Args)]
12+
pub struct ChangelogArgs {}
13+
14+
impl ChangelogArgs {
15+
pub async fn execute(self, session: &mut ChatSession) -> Result<ChatState, ChatError> {
16+
// Use the shared rendering function from util::ui
17+
ui::render_changelog_content(&mut session.stderr).map_err(|e| ChatError::Std(std::io::Error::other(e)))?;
18+
19+
Ok(ChatState::PromptUser {
20+
skip_printing_tools: true,
21+
})
22+
}
23+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod changelog;
12
pub mod clear;
23
pub mod compact;
34
pub mod context;
@@ -16,6 +17,7 @@ pub mod todos;
1617
pub mod tools;
1718
pub mod usage;
1819

20+
use changelog::ChangelogArgs;
1921
use clap::Parser;
2022
use clear::ClearArgs;
2123
use compact::CompactArgs;
@@ -75,6 +77,9 @@ pub enum SlashCommand {
7577
Tools(ToolsArgs),
7678
/// Create a new Github issue or make a feature request
7779
Issue(issue::IssueArgs),
80+
/// View changelog for Amazon Q CLI
81+
#[command(name = "changelog")]
82+
Changelog(ChangelogArgs),
7883
/// View and retrieve prompts
7984
Prompts(PromptsArgs),
8085
/// View context hooks
@@ -145,6 +150,7 @@ impl SlashCommand {
145150
skip_printing_tools: true,
146151
})
147152
},
153+
Self::Changelog(args) => args.execute(session).await,
148154
Self::Prompts(args) => args.execute(session).await,
149155
Self::Hooks(args) => args.execute(session).await,
150156
Self::Usage(args) => args.execute(os, session).await,
@@ -179,6 +185,7 @@ impl SlashCommand {
179185
Self::Compact(_) => "compact",
180186
Self::Tools(_) => "tools",
181187
Self::Issue(_) => "issue",
188+
Self::Changelog(_) => "changelog",
182189
Self::Prompts(_) => "prompts",
183190
Self::Hooks(_) => "hooks",
184191
Self::Usage(_) => "usage",

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ use crate::telemetry::{
167167
use crate::util::{
168168
MCP_SERVER_TOOL_DELIMITER,
169169
directories,
170+
ui,
170171
};
171172

172173
const LIMIT_REACHED_TEXT: &str = color_print::cstr! { "You've used all your free requests for this month. You have two options:
@@ -449,8 +450,11 @@ const WELCOME_TEXT: &str = color_print::cstr! {"<cyan!>
449450
const SMALL_SCREEN_WELCOME_TEXT: &str = color_print::cstr! {"<em>Welcome to <cyan!>Amazon Q</cyan!>!</em>"};
450451
const RESUME_TEXT: &str = color_print::cstr! {"<em>Picking up where we left off...</em>"};
451452

453+
// Maximum number of times to show the changelog announcement per version
454+
const CHANGELOG_MAX_SHOW_COUNT: i64 = 2;
455+
452456
// Only show the model-related tip for now to make users aware of this feature.
453-
const ROTATING_TIPS: [&str; 18] = [
457+
const ROTATING_TIPS: [&str; 19] = [
454458
color_print::cstr! {"You can resume the last conversation from your current directory by launching with
455459
<green!>q chat --resume</green!>"},
456460
color_print::cstr! {"Get notified whenever Q CLI finishes responding.
@@ -484,6 +488,7 @@ const ROTATING_TIPS: [&str; 18] = [
484488
color_print::cstr! {"Run <green!>/prompts</green!> to learn how to build & run repeatable workflows"},
485489
color_print::cstr! {"Use <green!>/tangent</green!> or <green!>ctrl + t</green!> (customizable) to start isolated conversations ( ↯ ) that don't affect your main chat history"},
486490
color_print::cstr! {"Ask me directly about my capabilities! Try questions like <green!>\"What can you do?\"</green!> or <green!>\"Can you save conversations?\"</green!>"},
491+
color_print::cstr! {"Stay up to date with the latest features and improvements! Use <green!>/changelog</green!> to see what's new in Amazon Q CLI"},
487492
];
488493

489494
const GREETING_BREAK_POINT: usize = 80;
@@ -1109,6 +1114,34 @@ impl ChatSession {
11091114

11101115
Ok(())
11111116
}
1117+
1118+
async fn show_changelog_announcement(&mut self, os: &mut Os) -> Result<()> {
1119+
let current_version = env!("CARGO_PKG_VERSION");
1120+
let last_version = os.database.get_changelog_last_version()?;
1121+
let show_count = os.database.get_changelog_show_count()?.unwrap_or(0);
1122+
1123+
// Check if version changed or if we haven't shown it max times yet
1124+
let should_show = match &last_version {
1125+
Some(last) if last == current_version => show_count < CHANGELOG_MAX_SHOW_COUNT,
1126+
_ => true, // New version or no previous version
1127+
};
1128+
1129+
if should_show {
1130+
// Use the shared rendering function
1131+
ui::render_changelog_content(&mut self.stderr)?;
1132+
1133+
// Update the database entries
1134+
os.database.set_changelog_last_version(current_version)?;
1135+
let new_count = if last_version.as_deref() == Some(current_version) {
1136+
show_count + 1
1137+
} else {
1138+
1
1139+
};
1140+
os.database.set_changelog_show_count(new_count)?;
1141+
}
1142+
1143+
Ok(())
1144+
}
11121145
}
11131146

11141147
impl Drop for ChatSession {
@@ -1255,6 +1288,9 @@ impl ChatSession {
12551288
execute!(self.stderr, style::Print("\n"), style::SetForegroundColor(Color::Reset))?;
12561289
}
12571290

1291+
// Check if we should show the whats-new announcement
1292+
self.show_changelog_announcement(os).await?;
1293+
12581294
if self.all_tools_trusted() {
12591295
queue!(
12601296
self.stderr,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub const COMMANDS: &[&str] = &[
9292
"/compact",
9393
"/compact help",
9494
"/usage",
95+
"/changelog",
9596
"/save",
9697
"/load",
9798
"/subscribe",

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ impl Introspect {
7171
documentation.push_str("\n\n--- docs/todo-lists.md ---\n");
7272
documentation.push_str(include_str!("../../../../../../docs/todo-lists.md"));
7373

74+
documentation.push_str("\n\n--- changelog (from feed.json) ---\n");
75+
// Include recent changelog entries from feed.json
76+
let feed = crate::cli::feed::Feed::load();
77+
let recent_entries = feed.get_all_changelogs().into_iter().take(5).collect::<Vec<_>>();
78+
for entry in recent_entries {
79+
documentation.push_str(&format!("\n## {} ({})\n", entry.version, entry.date));
80+
for change in &entry.changes {
81+
documentation.push_str(&format!("- {}: {}\n", change.change_type, change.description));
82+
}
83+
}
84+
7485
documentation.push_str("\n\n--- CONTRIBUTING.md ---\n");
7586
documentation.push_str(include_str!("../../../../../../CONTRIBUTING.md"));
7687

0 commit comments

Comments
 (0)