Add local project indexing and search guidance

This commit is contained in:
2026-08-30 22:00:27 -05:00
parent 88c1ef9716
commit 1c7d3c175d
39 changed files with 2094 additions and 306 deletions
@@ -226,6 +226,7 @@ impl GetFilesExecutor {
GetRelevantFilesError::Missing => {
"The current directory isn't within a git repository, which is necessary to search for relevant files.".to_owned()
}
GetRelevantFilesError::LocalIndexUnavailable(message) => message,
};
ActionExecution::Sync(AIAgentActionResultType::GetFiles(
GetFilesResult::Error(error_message),
@@ -1,6 +1,8 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[cfg(all(feature = "local_fs", not(target_family = "wasm")))]
use ai::index::local_project_index::LocalProjectIndexManager;
use futures::channel::oneshot;
use futures::future::BoxFuture;
use futures::FutureExt;
@@ -273,6 +275,7 @@ impl SearchCodebaseExecutor {
),
},
Err(e) => {
self.active_searches.remove(id);
log::warn!("Failed to send remote get_relevant_files request: {e:?}");
ActionExecution::Sync(AIAgentActionResultType::SearchCodebase(
SearchCodebaseResult::Failed {
@@ -385,6 +388,7 @@ impl SearchCodebaseExecutor {
),
},
Err(e) => {
self.active_searches.remove(id);
log::warn!("Failed to send get_relevant_files request for directory: {e:?}");
let error_message = match e {
@@ -397,6 +401,7 @@ impl SearchCodebaseExecutor {
GetRelevantFilesError::Missing => {
"The current directory isn't within a git repository, which is necessary to search for relevant files.".to_owned()
}
GetRelevantFilesError::LocalIndexUnavailable(message) => message,
};
ActionExecution::Sync(AIAgentActionResultType::SearchCodebase(
SearchCodebaseResult::Failed {
@@ -465,6 +470,13 @@ impl SearchCodebaseExecutor {
pwd
};
#[cfg(all(feature = "local_fs", not(target_family = "wasm")))]
if let Some(root) = app
.try_get_singleton_model_as_ref::<LocalProjectIndexManager>()
.and_then(|manager| manager.root_for_path(&search_dir))
{
return Some(root);
}
self.get_relevant_files_controller
.as_ref(app)
.root_directory_for_search(&search_dir, app)
@@ -15,7 +15,7 @@ use crate::ui_components::blended_colors;
use crate::ui_components::icons::Icon;
const SPEEDBUMP_HEADER: &str = "Index Codebase?";
const SPEEDBUMP_TEXT: &str = "Indexing helps agents quickly understand context and provide targeted solutions. Code is never stored on the server.";
const SPEEDBUMP_TEXT: &str = "Galaxy builds and searches this project index on your device. Source files are read from disk when results are returned to the agent.";
/// Uniform padding around the banner
const PADDING: f32 = 12.;
/// Text for the button that allows execution
@@ -1,7 +1,8 @@
use std::collections::HashMap;
use std::sync::Arc;
use ai::index::full_source_code_embedding::manager::CodebaseIndexManager;
#[cfg(all(feature = "local_fs", not(target_family = "wasm")))]
use ai::index::local_project_index::LocalProjectIndexManager;
use chrono::Local;
use galaxy_core::features::FeatureFlag;
use galaxy_graphql::generic_string_object::GenericStringObjectFormat as GraphQLFormat;
@@ -65,10 +66,12 @@ pub(super) fn input_context_for_request(
context.push(AIAgentContext::ExecutionEnvironment(env));
}
if FeatureFlag::FullSourceCodeEmbedding.is_enabled()
&& FeatureFlag::CrossRepoContext.is_enabled()
{
let session_context = SessionContext::from_session(active_session, app);
let session_context = SessionContext::from_session(active_session, app);
let should_add_codebase_context = FeatureFlag::CrossRepoContext.is_enabled()
&& ((session_context.is_remote() && FeatureFlag::FullSourceCodeEmbedding.is_enabled())
|| (!session_context.is_remote()
&& cfg!(all(feature = "local_fs", not(target_family = "wasm")))));
if should_add_codebase_context {
if session_context.is_remote() {
add_remote_codebase_context(&mut context, &session_context, app);
} else {
@@ -91,27 +94,28 @@ pub(super) fn input_context_for_request(
context.into()
}
#[cfg(all(feature = "local_fs", not(target_family = "wasm")))]
fn add_local_codebase_context(context: &mut Vec<AIAgentContext>, app: &AppContext) {
for (codebase_path, status) in
CodebaseIndexManager::as_ref(app).get_codebase_index_statuses(app)
{
// TODO(daniel): We should figure out a mechanism for handling stale codebases.
if status.has_synced_version() {
// For now, we pass the name of the directory as the name of the
// codebase.
for (codebase_path, status) in LocalProjectIndexManager::as_ref(app).statuses() {
if matches!(
status,
ai::index::local_project_index::LocalIndexStatus::Ready { .. }
) {
let codebase_name = codebase_path
.file_name()
.map(|name| name.to_string_lossy())
.unwrap_or_default();
context.push(AIAgentContext::Codebase {
name: codebase_name.into(),
path: codebase_path.to_string_lossy().into(),
})
});
}
}
}
#[cfg(any(not(feature = "local_fs"), target_family = "wasm"))]
fn add_local_codebase_context(_context: &mut Vec<AIAgentContext>, _app: &AppContext) {}
#[cfg(not(target_family = "wasm"))]
fn add_remote_codebase_context(
context: &mut Vec<AIAgentContext>,