Skip to content

Commit 4048339

Browse files
committed
feat: search symbol indexing
1 parent 8c7cfd1 commit 4048339

7 files changed

Lines changed: 1127 additions & 9 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ zip = { version = "0.6", default-features = false, features = ["deflate"] }
6666

6767
# Misc
6868
url = "2.5"
69+
regex = "1.10"
6970
regress = "0.10"
7071
include_dir = "0.7"
7172
base64 = "0.22"

server/packages/sandbox-agent/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ tracing-logfmt.workspace = true
3636
tracing-subscriber.workspace = true
3737
include_dir.workspace = true
3838
base64.workspace = true
39+
regex.workspace = true
3940
tempfile = { workspace = true, optional = true }
4041

4142
[target.'cfg(unix)'.dependencies]

server/packages/sandbox-agent/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod agent_server_logs;
44
pub mod credentials;
55
pub mod opencode_compat;
66
pub mod router;
7+
pub(crate) mod search;
78
pub mod server_logs;
89
pub mod telemetry;
910
pub mod ui;

server/packages/sandbox-agent/src/opencode_compat.rs

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
use std::collections::HashMap;
88
use std::convert::Infallible;
9+
use std::path::PathBuf;
910
use std::str::FromStr;
1011
use std::sync::atomic::{AtomicU64, Ordering};
1112
use std::sync::Arc;
@@ -24,6 +25,7 @@ use tokio::time::interval;
2425
use utoipa::{IntoParams, OpenApi, ToSchema};
2526

2627
use crate::router::{AppState, CreateSessionRequest, PermissionReply};
28+
use crate::search::{FileSearchType, SearchFileParams, SearchSymbolParams, SearchTextParams};
2729
use sandbox_agent_agent_management::agents::AgentId;
2830
use sandbox_agent_error::SandboxError;
2931
use sandbox_agent_universal_agent_schema::{
@@ -471,18 +473,26 @@ struct ToolQuery {
471473
struct FindTextQuery {
472474
directory: Option<String>,
473475
pattern: Option<String>,
476+
#[serde(rename = "caseSensitive")]
477+
case_sensitive: Option<bool>,
478+
limit: Option<usize>,
474479
}
475480

476481
#[derive(Debug, Deserialize, IntoParams)]
477482
struct FindFilesQuery {
478483
directory: Option<String>,
479484
query: Option<String>,
485+
dirs: Option<String>,
486+
#[serde(rename = "type")]
487+
kind: Option<String>,
488+
limit: Option<usize>,
480489
}
481490

482491
#[derive(Debug, Deserialize, IntoParams)]
483492
struct FindSymbolsQuery {
484493
directory: Option<String>,
485494
query: Option<String>,
495+
limit: Option<usize>,
486496
}
487497

488498
#[derive(Debug, Deserialize, IntoParams)]
@@ -3855,11 +3865,37 @@ async fn oc_file_status() -> impl IntoResponse {
38553865
responses((status = 200)),
38563866
tag = "opencode"
38573867
)]
3858-
async fn oc_find_text(Query(query): Query<FindTextQuery>) -> impl IntoResponse {
3859-
if query.pattern.is_none() {
3868+
async fn oc_find_text(
3869+
State(state): State<Arc<OpenCodeAppState>>,
3870+
headers: HeaderMap,
3871+
Query(query): Query<FindTextQuery>,
3872+
) -> impl IntoResponse {
3873+
let Some(pattern) = query.pattern else {
38603874
return bad_request("pattern is required").into_response();
3875+
};
3876+
3877+
let directory = state
3878+
.opencode
3879+
.directory_for(&headers, query.directory.as_ref());
3880+
let worktree = state.opencode.worktree_for(&directory);
3881+
let search_params = SearchTextParams {
3882+
root: PathBuf::from(worktree),
3883+
directory: PathBuf::from(directory),
3884+
pattern,
3885+
case_sensitive: query.case_sensitive,
3886+
limit: query.limit,
3887+
};
3888+
3889+
match state
3890+
.inner
3891+
.session_manager()
3892+
.search_service()
3893+
.search_text(search_params)
3894+
.await
3895+
{
3896+
Ok(matches) => (StatusCode::OK, Json(matches)).into_response(),
3897+
Err(err) => sandbox_error_response(err).into_response(),
38613898
}
3862-
(StatusCode::OK, Json(json!([]))).into_response()
38633899
}
38643900

38653901
#[utoipa::path(
@@ -3868,11 +3904,52 @@ async fn oc_find_text(Query(query): Query<FindTextQuery>) -> impl IntoResponse {
38683904
responses((status = 200)),
38693905
tag = "opencode"
38703906
)]
3871-
async fn oc_find_files(Query(query): Query<FindFilesQuery>) -> impl IntoResponse {
3872-
if query.query.is_none() {
3907+
async fn oc_find_files(
3908+
State(state): State<Arc<OpenCodeAppState>>,
3909+
headers: HeaderMap,
3910+
Query(query): Query<FindFilesQuery>,
3911+
) -> impl IntoResponse {
3912+
let Some(query_value) = query.query else {
38733913
return bad_request("query is required").into_response();
3914+
};
3915+
3916+
let include_dirs = match query.dirs.as_deref() {
3917+
Some("true") => Some(true),
3918+
Some("false") => Some(false),
3919+
Some(_) => return bad_request("dirs must be true or false").into_response(),
3920+
None => None,
3921+
};
3922+
3923+
let file_type = match query.kind.as_deref() {
3924+
Some("file") => Some(FileSearchType::File),
3925+
Some("directory") => Some(FileSearchType::Directory),
3926+
Some(_) => return bad_request("type must be file or directory").into_response(),
3927+
None => None,
3928+
};
3929+
3930+
let directory = state
3931+
.opencode
3932+
.directory_for(&headers, query.directory.as_ref());
3933+
let worktree = state.opencode.worktree_for(&directory);
3934+
let search_params = SearchFileParams {
3935+
root: PathBuf::from(worktree),
3936+
directory: PathBuf::from(directory),
3937+
query: query_value,
3938+
include_dirs,
3939+
file_type,
3940+
limit: query.limit,
3941+
};
3942+
3943+
match state
3944+
.inner
3945+
.session_manager()
3946+
.search_service()
3947+
.search_files(search_params)
3948+
.await
3949+
{
3950+
Ok(results) => (StatusCode::OK, Json(results)).into_response(),
3951+
Err(err) => sandbox_error_response(err).into_response(),
38743952
}
3875-
(StatusCode::OK, Json(json!([]))).into_response()
38763953
}
38773954

38783955
#[utoipa::path(
@@ -3881,11 +3958,36 @@ async fn oc_find_files(Query(query): Query<FindFilesQuery>) -> impl IntoResponse
38813958
responses((status = 200)),
38823959
tag = "opencode"
38833960
)]
3884-
async fn oc_find_symbols(Query(query): Query<FindSymbolsQuery>) -> impl IntoResponse {
3885-
if query.query.is_none() {
3961+
async fn oc_find_symbols(
3962+
State(state): State<Arc<OpenCodeAppState>>,
3963+
headers: HeaderMap,
3964+
Query(query): Query<FindSymbolsQuery>,
3965+
) -> impl IntoResponse {
3966+
let Some(query_value) = query.query else {
38863967
return bad_request("query is required").into_response();
3968+
};
3969+
3970+
let directory = state
3971+
.opencode
3972+
.directory_for(&headers, query.directory.as_ref());
3973+
let worktree = state.opencode.worktree_for(&directory);
3974+
let search_params = SearchSymbolParams {
3975+
root: PathBuf::from(worktree),
3976+
directory: PathBuf::from(directory),
3977+
query: query_value,
3978+
limit: query.limit,
3979+
};
3980+
3981+
match state
3982+
.inner
3983+
.session_manager()
3984+
.search_service()
3985+
.search_symbols(search_params)
3986+
.await
3987+
{
3988+
Ok(results) => (StatusCode::OK, Json(results)).into_response(),
3989+
Err(err) => sandbox_error_response(err).into_response(),
38873990
}
3888-
(StatusCode::OK, Json(json!([]))).into_response()
38893991
}
38903992

38913993
#[utoipa::path(

server/packages/sandbox-agent/src/router.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use utoipa::{Modify, OpenApi, ToSchema};
4040

4141
use crate::agent_server_logs::AgentServerLogs;
4242
use crate::opencode_compat::{build_opencode_router, OpenCodeAppState};
43+
use crate::search::SearchService;
4344
use crate::ui;
4445
use sandbox_agent_agent_management::agents::{
4546
AgentError as ManagerError, AgentId, AgentManager, InstallOptions, SpawnOptions, StreamingSpawn,
@@ -818,6 +819,7 @@ pub(crate) struct SessionManager {
818819
sessions: Mutex<Vec<SessionState>>,
819820
server_manager: Arc<AgentServerManager>,
820821
http_client: Client,
822+
search: SearchService,
821823
}
822824

823825
/// Shared Codex app-server process that handles multiple sessions via JSON-RPC.
@@ -1538,9 +1540,14 @@ impl SessionManager {
15381540
sessions: Mutex::new(Vec::new()),
15391541
server_manager,
15401542
http_client: Client::new(),
1543+
search: SearchService::new(),
15411544
}
15421545
}
15431546

1547+
pub(crate) fn search_service(&self) -> SearchService {
1548+
self.search.clone()
1549+
}
1550+
15441551
fn session_ref<'a>(sessions: &'a [SessionState], session_id: &str) -> Option<&'a SessionState> {
15451552
sessions
15461553
.iter()

0 commit comments

Comments
 (0)