Skip to content

Commit 95e2369

Browse files
committed
feat: add --semantic-query to all function-targeted commands
Adds natural-language entry point discovery via HNSW embeddings: - query::semantic module (Ollama embedding + HNSW search) - --semantic-query flag on paths, cfg, dominators, loops, patterns, frontiers, coverage, risk, suggest, hotpaths - resolve_function_or_semantic helper in storage::operations - MirageDb::path() getter for db path access
1 parent a42f7fe commit 95e2369

27 files changed

Lines changed: 680 additions & 168 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to Mirage are documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.8.0] - 2026-06-08
9+
10+
### Added
11+
12+
- **`--semantic-query` flag** for all function-targeted commands (`paths`, `cfg`, `dominators`, `loops`, `patterns`, `frontiers`, `coverage`, `risk`, `suggest`, `hotpaths`) — resolves natural-language queries to function entry points via HNSW embeddings (generated by Magellan, queried via Ollama). Falls back to exact `--function` name resolution when `--semantic-query` is not provided.
13+
- **`query::semantic` module** — Query embedding via Ollama, HNSW index search via sqlitegraph, entity resolution from `graph_entities`.
14+
- **`resolve_function_or_semantic()` helper** — Centralized resolution that checks HNSW index existence before calling Ollama, filters by `--file` disambiguation, and returns the top semantic match.
15+
816
## [1.7.0] - 2026-05-29
917

1018
### Added

Cargo.lock

Lines changed: 179 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mirage-analyzer"
3-
version = "1.7.0"
3+
version = "1.8.0"
44
edition = "2021"
55
rust-version = "1.70"
66
authors = ["Mirage Contributors"]
@@ -51,6 +51,10 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
5151
anyhow = "1.0"
5252
thiserror = "1.0"
5353

54+
# Semantic search
55+
ureq = "3"
56+
toml = "0.8"
57+
5458
# Utils
5559
blake3 = "1.5"
5660
sha2 = "0.10"

deny.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ allow = [
5050
"Zlib",
5151
"BSL-1.0",
5252
"Apache-2.0 WITH LLVM-exception",
53+
"CDLA-Permissive-2.0",
5354
]
5455
exceptions = [
5556
{ allow = ["OpenSSL"], name = "ring", version = "*" },

src/cli/cmds/cfg_cmd.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use crate::output;
33
use anyhow::Result;
44

55
pub fn cfg(args: &CfgArgs, cli: &Cli) -> Result<()> {
6+
use crate::cfg::load_cfg_from_db;
67
use crate::cfg::{export_dot, export_json, CFGExport};
7-
use crate::cfg::{load_cfg_from_db, resolve_function_name_with_file};
8+
use crate::storage::resolve_function_or_semantic;
89
use crate::storage::MirageDb;
910

1011
// Resolve database path
@@ -28,26 +29,31 @@ pub fn cfg(args: &CfgArgs, cli: &Cli) -> Result<()> {
2829
}
2930
};
3031

31-
// Resolve function name/ID to function_id (with optional file filter)
32-
let function_id =
33-
match resolve_function_name_with_file(&db, &args.function, args.file.as_deref()) {
34-
Ok(id) => id,
35-
Err(_e) => {
36-
if matches!(cli.output, OutputFormat::Json | OutputFormat::Pretty) {
37-
let error = output::JsonError::function_not_found(&args.function);
38-
let wrapper = output::JsonResponse::new(error);
39-
println!("{}", wrapper.to_json());
40-
std::process::exit(output::EXIT_DATABASE);
41-
} else {
42-
output::error(&format!(
43-
"Function '{}' not found in database",
44-
args.function
45-
));
46-
output::info(&format!("Hint: {}", output::R_HINT_LIST_FUNCTIONS));
47-
std::process::exit(output::EXIT_DATABASE);
48-
}
32+
// Resolve function name/ID or semantic query to function_id (with optional file filter)
33+
let function_id = match resolve_function_or_semantic(
34+
&db,
35+
&args.function,
36+
args.semantic_query.as_deref(),
37+
args.file.as_deref(),
38+
) {
39+
Ok(id) => id,
40+
Err(e) => {
41+
if matches!(cli.output, OutputFormat::Json | OutputFormat::Pretty) {
42+
let error = output::JsonError::new(
43+
"FunctionNotFound",
44+
&format!("{}", e),
45+
output::E_CFG_ERROR,
46+
);
47+
let wrapper = output::JsonResponse::new(error);
48+
println!("{}", wrapper.to_json());
49+
std::process::exit(output::EXIT_DATABASE);
50+
} else {
51+
output::error(&format!("{}", e));
52+
output::info(&format!("Hint: {}", output::R_HINT_LIST_FUNCTIONS));
53+
std::process::exit(output::EXIT_DATABASE);
4954
}
50-
};
55+
}
56+
};
5157

5258
// Load CFG from database
5359
let cfg = match load_cfg_from_db(&db, function_id) {

0 commit comments

Comments
 (0)