Fix tool results visibility, dynamic system prompt, rebrand Galaxy AI to Galaxy

- Rewrite extract_system_prompt to dynamically include CWD, OS, shell, git,
  project rules, and tool usage guidance from request context
- Fix extract_tool_result_content and format_tool_call_result to properly
  handle Grep, FileGlobV2, and ApplyFileDiffs results (were returning empty
  'Tool completed successfully' strings - model never saw file lists)
- Rename all 'Galaxy AI' references to just 'Galaxy' (menu, window title,
  bundle names, plist, welcome text, about)
- Update smoke test with file visibility test prompt
- Suppress dead_code warnings on unused smoke test functions
This commit is contained in:
Ryan Ward
2026-05-12 09:42:00 -05:00
parent c2c6b3bc8a
commit 0009f1366a
14 changed files with 353 additions and 36 deletions
+10 -10
View File
@@ -934,8 +934,8 @@ cloud_mode_input_v2 = ["cloud_mode"]
[package.metadata.bundle.bin.warp-oss]
category = "public.app-category.developer-tools"
copyright = "© 2025, Denver Technologies, Inc"
identifier = "dev.warp.WarpOss"
name = "WarpOss"
identifier = "dev.galaxy.GalaxyOss"
name = "Galaxy"
resources = ["assets/onboarding"]
icon = ["channels/oss/icon/no-padding/512x512.png", "channels/oss/icon/no-padding/icon.ico"]
short_description = "The open-source, cloud-backed terminal for individuals and teams."
@@ -943,8 +943,8 @@ short_description = "The open-source, cloud-backed terminal for individuals and
[package.metadata.bundle.bin.stable]
category = "public.app-category.developer-tools"
copyright = "© 2025, Denver Technologies, Inc"
identifier = "dev.warp.Warp-Stable"
name = "Warp"
identifier = "dev.galaxy.Galaxy-Stable"
name = "Galaxy"
osx_frameworks = [
"frameworks/default/Sentry-Dynamic-WithARM64e.xcframework/macos-arm64_arm64e_x86_64/Sentry.framework",
]
@@ -954,8 +954,8 @@ short_description = "The cloud-backed terminal for individuals and teams, stable
[package.metadata.bundle.bin.preview]
category = "public.app-category.developer-tools"
copyright = "© 2025, Denver Technologies, Inc"
identifier = "dev.warp.Warp-Preview"
name = "WarpPreview"
identifier = "dev.galaxy.Galaxy-Preview"
name = "GalaxyPreview"
osx_frameworks = [
"frameworks/default/Sentry-Dynamic-WithARM64e.xcframework/macos-arm64_arm64e_x86_64/Sentry.framework",
]
@@ -965,8 +965,8 @@ short_description = "The cloud-backed terminal for individuals and teams, featur
[package.metadata.bundle.bin.dev]
category = "public.app-category.developer-tools"
copyright = "© 2025, Denver Technologies, Inc"
identifier = "dev.warp.Warp-Dev"
name = "WarpDev"
identifier = "dev.galaxy.Galaxy-Dev"
name = "GalaxyDev"
osx_frameworks = [
"frameworks/dev/Sentry-Dynamic-WithARM64e.xcframework/macos-arm64_arm64e_x86_64/Sentry.framework",
]
@@ -976,8 +976,8 @@ short_description = "The cloud-backed terminal for individuals and teams, develo
[package.metadata.bundle.bin.warp]
category = "public.app-category.developer-tools"
copyright = "© 2025, Denver Technologies, Inc"
identifier = "dev.warp.Warp-Local"
name = "WarpLocal"
identifier = "dev.galaxy.Galaxy-Local"
name = "GalaxyLocal"
resources = ["assets/onboarding"]
short_description = "The cloud-backed terminal for individuals and teams, developer build."
+231 -7
View File
@@ -551,8 +551,80 @@ fn ensure_tool_results_paired(messages: &mut Vec<ConversationMessage>) {
}
}
pub fn extract_system_prompt(_request: &api::Request) -> Option<String> {
Some("You are a helpful AI coding assistant. You help users with software engineering tasks including writing code, debugging, and explaining concepts.".to_string())
pub fn extract_system_prompt(request: &api::Request) -> Option<String> {
let mut prompt = String::with_capacity(2048);
prompt.push_str("You are Galaxy, an AI coding assistant embedded in a terminal application. You help users with software engineering tasks including writing code, debugging, explaining concepts, and navigating codebases.\n\n");
if let Some(input) = &request.input {
if let Some(context) = &input.context {
prompt.push_str("## Environment\n");
if let Some(dir) = &context.directory {
if !dir.pwd.is_empty() {
prompt.push_str(&format!("- Working directory: {}\n", dir.pwd));
}
if !dir.home.is_empty() {
prompt.push_str(&format!("- Home directory: {}\n", dir.home));
}
}
if let Some(os) = &context.operating_system {
if !os.platform.is_empty() {
prompt.push_str(&format!("- OS: {}\n", os.platform));
}
}
if let Some(shell) = &context.shell {
if !shell.name.is_empty() {
prompt.push_str(&format!("- Shell: {}", shell.name));
if !shell.version.is_empty() {
prompt.push_str(&format!(" {}", shell.version));
}
prompt.push('\n');
}
}
if let Some(git) = &context.git {
if !git.branch.is_empty() {
prompt.push_str(&format!("- Git branch: {}\n", git.branch));
}
}
if let Some(ts) = &context.current_time {
prompt.push_str(&format!("- Current time (UTC): {}\n", ts));
}
prompt.push('\n');
if !context.project_rules.is_empty() {
prompt.push_str("## Project Rules\n");
for rules in &context.project_rules {
if !rules.root_path.is_empty() {
prompt.push_str(&format!("### Rules from {}\n", rules.root_path));
}
for file in &rules.active_rule_files {
if !file.content.is_empty() {
prompt.push_str(&file.content);
prompt.push('\n');
}
}
}
prompt.push('\n');
}
}
}
prompt.push_str("## Tools\nYou have access to the following tools. Use them proactively to explore codebases and complete tasks:\n");
prompt.push_str("- `run_shell_command`: Execute shell commands. Use absolute paths based on the working directory.\n");
prompt.push_str("- `read_files`: Read file contents. Pass all files you need in a single call.\n");
prompt.push_str("- `apply_file_diffs`: Apply search/replace edits to files.\n");
prompt.push_str("- `grep`: Search for patterns in files. Pass all patterns in one call.\n");
prompt.push_str("- `file_glob`: Find files matching glob patterns. Pass all patterns in one call.\n");
prompt.push_str("- `get_tool_documentation`: Get detailed documentation for any tool or system capabilities.\n\n");
prompt.push_str("## Guidelines\n");
prompt.push_str("- ALWAYS use tools to explore the codebase before answering questions about code.\n");
prompt.push_str("- Use absolute paths based on the working directory shown above.\n");
prompt.push_str("- When asked about a project, start by listing files with `file_glob` or `run_shell_command`.\n");
prompt.push_str("- Read relevant files before making claims about code structure or behavior.\n");
prompt.push_str("- Be concise and direct in responses.\n");
Some(prompt)
}
pub fn extract_tools(request: &api::Request) -> Vec<ToolDefinition> {
@@ -773,7 +845,11 @@ fn extract_tool_result_content(result: &api::request::input::ToolCallResult) ->
api::request::input::tool_call_result::Result::RunShellCommand(cmd_result) => {
match &cmd_result.result {
Some(api::run_shell_command_result::Result::CommandFinished(finished)) => {
finished.output.clone()
if finished.output.is_empty() {
format!("Exit code: {}\n(no output)", finished.exit_code)
} else {
format!("Exit code: {}\n{}", finished.exit_code, finished.output)
}
}
Some(api::run_shell_command_result::Result::LongRunningCommandSnapshot(
snapshot,
@@ -803,6 +879,78 @@ fn extract_tool_result_content(result: &api::request::input::ToolCallResult) ->
_ => "Failed to read files.".to_string(),
}
}
api::request::input::tool_call_result::Result::Grep(grep_result) => {
match &grep_result.result {
Some(api::grep_result::Result::Success(success)) => {
if success.matched_files.is_empty() {
"No matches found.".to_string()
} else {
success
.matched_files
.iter()
.map(|f| {
let lines: String = f
.matched_lines
.iter()
.map(|l| format!(" line {}", l.line_number))
.collect::<Vec<_>>()
.join(", ");
format!("{} (matches at: {})", f.file_path, lines)
})
.collect::<Vec<_>>()
.join("\n")
}
}
Some(api::grep_result::Result::Error(error)) => {
format!("Grep error: {}", error.message)
}
None => "Grep completed (no result).".to_string(),
}
}
api::request::input::tool_call_result::Result::FileGlobV2(glob_result) => {
match &glob_result.result {
Some(api::file_glob_v2_result::Result::Success(success)) => {
if success.matched_files.is_empty() {
"No files matched.".to_string()
} else {
success
.matched_files
.iter()
.map(|f| f.file_path.as_str())
.collect::<Vec<_>>()
.join("\n")
}
}
Some(api::file_glob_v2_result::Result::Error(error)) => {
format!("File glob error: {}", error.message)
}
None => "File glob completed (no result).".to_string(),
}
}
api::request::input::tool_call_result::Result::ApplyFileDiffs(diff_result) => {
match &diff_result.result {
Some(api::apply_file_diffs_result::Result::Success(success)) => {
let mut parts = Vec::new();
for f in &success.updated_files_v2 {
if let Some(file) = &f.file {
parts.push(format!("Updated: {}", file.file_path));
}
}
for f in &success.deleted_files {
parts.push(format!("Deleted: {}", f.file_path));
}
if parts.is_empty() {
"Diffs applied successfully.".to_string()
} else {
parts.join("\n")
}
}
Some(api::apply_file_diffs_result::Result::Error(error)) => {
format!("Apply diffs error: {}", error.message)
}
None => "Apply diffs completed.".to_string(),
}
}
_ => "Tool completed successfully.".to_string(),
}
} else {
@@ -816,10 +964,14 @@ fn format_tool_call_result(result: &api::message::ToolCallResult) -> String {
api::message::tool_call_result::Result::RunShellCommand(cmd_result) => {
match &cmd_result.result {
Some(api::run_shell_command_result::Result::CommandFinished(finished)) => {
format!(
"Exit code: {}\nOutput: {}",
finished.exit_code, finished.output
)
if finished.output.is_empty() {
format!("Exit code: {}\n(no output)", finished.exit_code)
} else {
format!(
"Exit code: {}\n{}",
finished.exit_code, finished.output
)
}
}
Some(api::run_shell_command_result::Result::LongRunningCommandSnapshot(
snapshot,
@@ -840,6 +992,78 @@ fn format_tool_call_result(result: &api::message::ToolCallResult) -> String {
_ => "Read files completed.".to_string(),
}
}
api::message::tool_call_result::Result::Grep(grep_result) => {
match &grep_result.result {
Some(api::grep_result::Result::Success(success)) => {
if success.matched_files.is_empty() {
"No matches found.".to_string()
} else {
success
.matched_files
.iter()
.map(|f| {
let lines: String = f
.matched_lines
.iter()
.map(|l| format!(" line {}", l.line_number))
.collect::<Vec<_>>()
.join(", ");
format!("{} (matches at: {})", f.file_path, lines)
})
.collect::<Vec<_>>()
.join("\n")
}
}
Some(api::grep_result::Result::Error(error)) => {
format!("Grep error: {}", error.message)
}
None => "Grep completed (no result).".to_string(),
}
}
api::message::tool_call_result::Result::FileGlobV2(glob_result) => {
match &glob_result.result {
Some(api::file_glob_v2_result::Result::Success(success)) => {
if success.matched_files.is_empty() {
"No files matched.".to_string()
} else {
success
.matched_files
.iter()
.map(|f| f.file_path.as_str())
.collect::<Vec<_>>()
.join("\n")
}
}
Some(api::file_glob_v2_result::Result::Error(error)) => {
format!("File glob error: {}", error.message)
}
None => "File glob completed (no result).".to_string(),
}
}
api::message::tool_call_result::Result::ApplyFileDiffs(diff_result) => {
match &diff_result.result {
Some(api::apply_file_diffs_result::Result::Success(success)) => {
let mut parts = Vec::new();
for f in &success.updated_files_v2 {
if let Some(file) = &f.file {
parts.push(format!("Updated: {}", file.file_path));
}
}
for f in &success.deleted_files {
parts.push(format!("Deleted: {}", f.file_path));
}
if parts.is_empty() {
"Diffs applied successfully.".to_string()
} else {
parts.join("\n")
}
}
Some(api::apply_file_diffs_result::Result::Error(error)) => {
format!("Apply diffs error: {}", error.message)
}
None => "Apply diffs completed.".to_string(),
}
}
api::message::tool_call_result::Result::Server(server_result) => {
server_result.serialized_result.clone()
}
+1 -1
View File
@@ -1,4 +1,4 @@
const CAPABILITIES_DOC: &str = r#"# Galaxy AI — System Capabilities
const CAPABILITIES_DOC: &str = r#"# Galaxy — System Capabilities
You are Galaxy, an AI coding assistant embedded in a terminal application with direct filesystem and shell access.
+3 -3
View File
@@ -1,4 +1,4 @@
//! AI Assistant has since been renamed to "Galaxy AI" in the product.
//! AI Assistant has since been renamed to "Galaxy" in the product.
use std::{collections::HashSet, sync::Arc};
use crate::{
@@ -34,8 +34,8 @@ mod test_util;
/// This is also roughly the limit at which the editor starts degrading.
pub const PROMPT_CHARACTER_LIMIT: usize = 1000;
pub const AI_ASSISTANT_FEATURE_NAME: &str = "Galaxy AI";
pub const ASK_AI_ASSISTANT_TEXT: &str = "Ask Galaxy AI";
pub const AI_ASSISTANT_FEATURE_NAME: &str = "Galaxy";
pub const ASK_AI_ASSISTANT_TEXT: &str = "Ask Galaxy";
pub const AI_ASSISTANT_SVG_PATH: &str = "bundled/svg/ai-assistant.svg";
+2 -2
View File
@@ -165,14 +165,14 @@ pub fn init(app: &mut AppContext) {
.with_key_binding(cmd_or_ctrl_shift("l")),
EditableBinding::new(
"ai_assistant_panel:reset_context",
"Restart Galaxy AI",
"Restart Galaxy",
AIAssistantAction::ResetContext,
)
.with_context_predicate(id!("AIAssistantPanel"))
.with_key_binding("ctrl-l"),
EditableBinding::new(
"ai_assistant_panel:reset_context",
"Restart Galaxy AI",
"Restart Galaxy",
AIAssistantAction::ResetContext,
)
.with_context_predicate(id!("AIAssistantPanel"))
+1 -1
View File
@@ -243,7 +243,7 @@ fn make_new_app_menu(ctx: &AppContext) -> Menu {
None,
)));
menu_items.push(MenuItem::Standard(StandardAction::Quit));
Menu::new("Galaxy AI", menu_items)
Menu::new("Galaxy", menu_items)
}
fn make_new_file_menu(ctx: &AppContext) -> Menu {
+94 -1
View File
@@ -11,6 +11,20 @@ use crate::workspace::Workspace;
use crate::BlocklistAIHistoryModel;
const TARGET_DIR: &str = "~/GIT/stitcher/stitcher";
const FILE_VISIBILITY_QUERY: &str = r#"/agent Okay, let's try again. Can you see files now? List the files in the current directory using your tools. Then respond with EXACTLY this structured format:
results: yes
file_count: <number of files/directories you found>
If you cannot see any files or your tools fail, respond with:
results: no
file_count: 0
You MUST use the file_glob or run_shell_command tool to check the directory contents first, then provide the structured response."#;
#[allow(dead_code)]
const AGENT_QUERY: &str = r#"/agent Analyze this project and respond with EXACTLY this structured format at the end of your response:
Answer: <a 2-3 sentence summary of how this project handles video uploads and media conversions>
@@ -49,11 +63,89 @@ fn run_cd(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId) {
ctx.spawn(
async move { Timer::after(CD_SETTLE_DELAY).await },
move |_ws: &mut Workspace, _, ctx| {
submit_query(ctx, window_id);
submit_file_visibility_test(ctx, window_id);
},
);
}
fn submit_file_visibility_test(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId) {
log::info!("[smoke-test] === FILE VISIBILITY TEST ===");
log::info!("[smoke-test] Submitting file visibility query...");
let terminal_view = get_terminal_view(ctx, window_id);
terminal_view.update(ctx, |view, ctx| {
let input = view.input().clone();
input.update(ctx, |input, ctx| {
input.submit_queued_prompt(FILE_VISIBILITY_QUERY.to_string(), ctx);
});
});
poll_file_visibility(ctx, window_id, std::time::Instant::now());
}
fn poll_file_visibility(
ctx: &mut galaxyui::ViewContext<Workspace>,
window_id: WindowId,
start: std::time::Instant,
) {
ctx.spawn(
async move { Timer::after(POLL_INTERVAL).await },
move |_ws: &mut Workspace, _, ctx| {
let elapsed = start.elapsed();
if elapsed > MAX_WAIT {
log::error!("[smoke-test] FILE VISIBILITY TEST TIMEOUT after {:?}", elapsed);
std::process::exit(1);
}
let terminal_view = get_terminal_view(ctx, window_id);
if let Some(full_text) = get_finished_text(ctx, &terminal_view) {
log::info!("[smoke-test] === FILE VISIBILITY RESULT ({:.1}s) ===", elapsed.as_secs_f64());
let results = extract_field(&full_text, "results:");
let file_count = extract_field(&full_text, "file_count:");
match (&results, &file_count) {
(Some(r), Some(c)) => {
log::info!("[smoke-test] results: {}", r);
log::info!("[smoke-test] file_count: {}", c);
if *r == "yes" {
let count: u32 = c.parse().unwrap_or(0);
if count > 0 {
log::info!("[smoke-test] === FILE VISIBILITY TEST PASSED (found {} files) ===", count);
log::info!("[smoke-test] LLM can see files. Proceeding to main test...");
std::process::exit(0);
} else {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (results=yes but file_count=0) ===");
std::process::exit(1);
}
} else {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (results=no) ===");
log::error!("[smoke-test] The LLM cannot see files in {}", TARGET_DIR);
log::error!("[smoke-test] Full response:");
for line in full_text.lines() {
log::error!("[smoke-test] {}", line);
}
std::process::exit(1);
}
}
_ => {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (missing structured fields) ===");
log::error!("[smoke-test] Full response:");
for line in full_text.lines() {
log::error!("[smoke-test] {}", line);
}
std::process::exit(1);
}
}
}
poll_file_visibility(ctx, window_id, start);
},
);
}
#[allow(dead_code)]
fn submit_query(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId) {
log::info!("[smoke-test] Submitting query...");
@@ -69,6 +161,7 @@ fn submit_query(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId)
poll(ctx, window_id, std::time::Instant::now());
}
#[allow(dead_code)]
fn poll(
ctx: &mut galaxyui::ViewContext<Workspace>,
window_id: WindowId,
+3 -3
View File
@@ -35,7 +35,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>Galaxy AI</string>
<string>Galaxy</string>
<key>CFBundleExecutable</key>
<string>galaxy-ai</string>
<key>CFBundleIdentifier</key>
@@ -43,7 +43,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Galaxy AI</string>
<string>Galaxy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -55,7 +55,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>UIDesignRequiresCompatibility</key>
<true/>
<key>CFBundleURLTypes</key>
<array><dict><key>CFBundleURLName</key><string>Galaxy AI</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array>
<array><dict><key>CFBundleURLName</key><string>Galaxy</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array>
<key>NSHumanReadableCopyright</key>
<string>© 2026, Samsung Electronics Co., Ltd.</string>
</dict>
+3 -3
View File
@@ -39,7 +39,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>Galaxy AI</string>
<string>Galaxy</string>
<key>CFBundleExecutable</key>
<string>galaxy-ai-oss</string>
<key>CFBundleIdentifier</key>
@@ -47,7 +47,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Galaxy AI</string>
<string>Galaxy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -59,7 +59,7 @@ embed_plist::embed_info_plist_bytes!(r#"
<key>UIDesignRequiresCompatibility</key>
<true/>
<key>CFBundleURLTypes</key>
<array><dict><key>CFBundleURLName</key><string>Galaxy AI</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array>
<array><dict><key>CFBundleURLName</key><string>Galaxy</string><key>CFBundleURLSchemes</key><array><string>galaxyai</string></array></dict></array>
<key>NSHumanReadableCopyright</key>
<string>© 2026, Samsung Electronics Co., Ltd.</string>
</dict>
+1 -1
View File
@@ -229,7 +229,7 @@ impl GetStartedView {
.finish(),
appearance
.ui_builder()
.paragraph("Welcome to Galaxy AI")
.paragraph("Welcome to Galaxy")
.with_style(UiComponentStyles {
font_size: Some(20.),
..Default::default()
+1 -1
View File
@@ -121,7 +121,7 @@ use galaxyui::{FocusContext, NextNewWindowsHasThisWindowsBoundsUponClose};
#[cfg(target_family = "wasm")]
use crate::auth::web_handoff::{WebHandoffEvent, WebHandoffView};
const WINDOW_TITLE: &str = "Galaxy AI";
const WINDOW_TITLE: &str = "Galaxy";
lazy_static! {
static ref FALLBACK_WINDOW_SIZE: Vector2F = vec2f(800.0, 600.0);
+1 -1
View File
@@ -1,6 +1,6 @@
use url::Url;
const DEFAULT_TITLE: &str = "Galaxy AI";
const DEFAULT_TITLE: &str = "Galaxy";
const BASE_APP_PATH: &str = "/app";
pub fn update_browser_url(url: Option<Url>, force_redirect: bool) {
+1 -1
View File
@@ -1396,7 +1396,7 @@ fn add_open_setting_pages_as_editable_binding(app: &mut AppContext) {
EditableBinding::new(
"workspace:show_settings_about_page",
BindingDescription::new("Open Settings: About")
.with_custom_description(bindings::MAC_MENUS_CONTEXT, "About Galaxy AI"),
.with_custom_description(bindings::MAC_MENUS_CONTEXT, "About Galaxy"),
WorkspaceAction::ShowSettingsPage(SettingsSection::About),
)
.with_group(bindings::BindingGroup::Settings.as_str())
+1 -1
View File
@@ -545,7 +545,7 @@ const TAB_BAR_PILL_WIDTH: f32 = 100.;
const PILL_FONT_SIZE: f32 = 12.;
// We use the word "Warp" in the Update Ready button to make it obvious that the terminal is Warp.
// This can lead to free advertising when users screen-share Warp when an update is available.
const UPDATE_READY_TEXT: &str = "Update Galaxy AI";
const UPDATE_READY_TEXT: &str = "Update Galaxy";
const TAB_BAR_OVERFLOW_MENU_WIDTH: f32 = 300.;