feat: add desktop document search and verified native CPU embeddings
Core CI / core (ubuntu-latest) (push) Failing after 1m22s
Core CI / core (macos-latest) (push) Canceled after 0s
Core CI / core (windows-latest) (push) Canceled after 0s
Core CI / desktop (ubuntu-latest) (push) Successful in 3m46s
Core CI / desktop (macos-latest) (push) Canceled after 0s
Core CI / desktop (windows-latest) (push) Canceled after 0s

This commit is contained in:
karl committed 2026-10-12 16:07:53 +02:00
1 parent f77112db97
commit a9b6592330
46 files changed
+10794 -77

No files matched your search

+49 -6
View File
@@ -4,7 +4,7 @@ use std::path::PathBuf;
use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand};
use searchi_core::SearchIndex;
use searchi_core::{IndexOptions, SearchIndex, SearchOptions};
#[derive(Debug, Parser)]
#[command(name = "searchi", version, about = "Search your local files offline")]
@@ -23,6 +23,9 @@ enum Command {
/// Folder to index recursively.
#[arg(long)]
root: PathBuf,
/// Additional directory names to exclude; repeat this flag as needed.
#[arg(long)]
exclude: Vec<String>,
},
/// Search filenames and indexed document content.
Search {
@@ -36,6 +39,21 @@ enum Command {
/// Emit a JSON array for scripting.
#[arg(long)]
json: bool,
/// Restrict results to this folder and descendants.
#[arg(long)]
folder: Option<PathBuf>,
/// File extension, with or without the leading dot.
#[arg(long)]
extension: Option<String>,
#[arg(long)]
min_size: Option<u64>,
#[arg(long)]
max_size: Option<u64>,
/// Inclusive modification time in Unix milliseconds.
#[arg(long)]
modified_after_ms: Option<i64>,
#[arg(long)]
modified_before_ms: Option<i64>,
},
/// Show the indexed file and content counts.
Stats {
@@ -48,15 +66,17 @@ enum Command {
fn main() -> Result<()> {
match Cli::parse().command {
Command::Index { db, root } => {
Command::Index { db, root, exclude } => {
let mut options = IndexOptions::default();
options.excluded_directory_names.extend(exclude);
let mut index = SearchIndex::open(&db)
.with_context(|| format!("Cannot open index {}", db.display()))?;
let report = index
.index_folder(&root)
.index_folder_with_options(&root, &options)
.with_context(|| format!("Cannot index folder {}", root.display()))?;
println!(
"Discovered: {} Indexed: {} Removed: {} Skipped: {}",
report.discovered, report.indexed, report.removed, report.skipped
"Discovered: {} Indexed: {} Unchanged: {} Removed: {} Skipped: {} Extraction failures: {}",
report.discovered, report.indexed, report.unchanged, report.removed, report.skipped, report.failures
);
}
Command::Search {
@@ -64,6 +84,12 @@ fn main() -> Result<()> {
query,
limit,
json,
folder,
extension,
min_size,
max_size,
modified_after_ms,
modified_before_ms,
} => {
if query.trim().is_empty() {
bail!("Search text cannot be empty");
@@ -72,7 +98,18 @@ fn main() -> Result<()> {
let index = SearchIndex::open(&db)
.with_context(|| format!("Cannot open index {}", db.display()))?;
let results = index
.search(&query, usize::from(limit))
.search_with_options(
&query,
usize::from(limit),
&SearchOptions {
folder,
extension,
min_size,
max_size,
modified_after_unix_ms: modified_after_ms,
modified_before_unix_ms: modified_before_ms,
},
)
.context("Search failed")?;
if json {
println!("{}", serde_json::to_string_pretty(&results)?);
@@ -81,6 +118,12 @@ fn main() -> Result<()> {
} else {
for result in results {
println!("{} [{}]", result.path, result.match_kind);
if let Some(source) = result.source {
println!(" {} {}", source.kind, source.number);
}
if let Some(error) = result.extraction_error {
println!(" Text extraction unavailable: {error}");
}
if !result.snippet.is_empty() {
println!(" {}", result.snippet.replace(['\r', '\n'], " "));
}