Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f0a76dc
fix: switch centroid updates from EMA to online mean
devwhodevs Mar 25, 2026
bed8fe0
feat: add created_by column and update_file_path store method
devwhodevs Mar 25, 2026
db730c6
feat: add get_chunk_vectors_for_file and placement_corrections table
devwhodevs Mar 25, 2026
b3f2403
feat: add created_by filter to list_files and MCP list tool
devwhodevs Mar 25, 2026
900e43f
refactor: extract index_file from run_index
devwhodevs Mar 25, 2026
c6ab868
refactor: extract remove_file and rename_file from indexer
devwhodevs Mar 25, 2026
bc47387
refactor: add run_index_shared for external store/embedder
devwhodevs Mar 25, 2026
cbb394a
feat: add watcher module skeleton with notify dependencies
devwhodevs Mar 25, 2026
9f75f9f
feat: implement watcher producer thread with notify-debouncer-full
devwhodevs Mar 25, 2026
f9dec79
feat: implement watcher consumer with move detection
devwhodevs Mar 25, 2026
f5e4b9a
feat: integrate file watcher into engraph serve
devwhodevs Mar 25, 2026
a4beaff
feat: placement correction learning on file moves
devwhodevs Mar 25, 2026
8040bc0
feat: add FuzzyName and FirstName link match types
devwhodevs Mar 25, 2026
f770d8d
feat: sliding window fuzzy link matching
devwhodevs Mar 25, 2026
0a5dfc6
feat: first-name matching for People notes
devwhodevs Mar 25, 2026
c946acd
chore: reserve link_skiplist table schema for future use
devwhodevs Mar 25, 2026
4efc9e4
style: cargo fmt
devwhodevs Mar 25, 2026
51bb097
fix: use compute_file_hash for consistent hashing in run_index_inner
devwhodevs Mar 25, 2026
a518ea3
perf: batch transactions in run_index_inner, per-file in watcher
devwhodevs Mar 25, 2026
95805b5
fix: adjust centroids on Changed/Deleted events, document FullRescan
devwhodevs Mar 25, 2026
5f0430d
fix: release store lock before frontmatter I/O, wrap remove_file in txn
devwhodevs Mar 25, 2026
96e26b7
fix: clippy collapsible_if warnings in watcher
devwhodevs Mar 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 238 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ strsim = "0.11"
ignore = "0.4"
rmcp = { version = "1.2", features = ["transport-io"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
notify = "7.0"
notify-debouncer-full = "0.4"

[dev-dependencies]
tempfile = "3"
39 changes: 25 additions & 14 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ pub fn context_list(
params: &ContextParams,
folder: Option<&str>,
tags: &[String],
created_by: Option<&str>,
limit: usize,
) -> Result<Vec<NoteListItem>> {
let files = params.store.list_files(folder, tags, limit)?;
let files = params.store.list_files(folder, tags, created_by, limit)?;
let file_ids: Vec<i64> = files.iter().map(|f| f.id).collect();
let edge_counts = params
.store
Expand Down Expand Up @@ -364,7 +365,7 @@ pub fn context_project(params: &ContextParams, name: &str) -> Result<ProjectCont

// Files in same folder
if let Some(folder) = &project_folder {
let folder_files = params.store.list_files(Some(folder), &[], 50)?;
let folder_files = params.store.list_files(Some(folder), &[], None, 50)?;
for f in folder_files {
if Some(f.id) != project_id && child_ids.insert(f.id) {
child_records.push(f);
Expand Down Expand Up @@ -664,9 +665,11 @@ mod tests {
let d1 = generate_docid("note.md");
let d2 = generate_docid("other.md");
store
.insert_file("note.md", "h1", 100, &["rust".into()], &d1)
.insert_file("note.md", "h1", 100, &["rust".into()], &d1, None)
.unwrap();
store
.insert_file("other.md", "h2", 100, &[], &d2, None)
.unwrap();
store.insert_file("other.md", "h2", 100, &[], &d2).unwrap();

let f1 = store.get_file("note.md").unwrap().unwrap().id;
let f2 = store.get_file("other.md").unwrap().unwrap().id;
Expand Down Expand Up @@ -712,7 +715,7 @@ mod tests {
fn test_read_file_not_on_disk() {
let (_tmp, store, root) = setup_vault();
store
.insert_file("ghost.md", "h3", 100, &[], "ggg333")
.insert_file("ghost.md", "h3", 100, &[], "ggg333", None)
.unwrap();
let params = ContextParams {
store: &store,
Expand Down Expand Up @@ -743,7 +746,7 @@ mod tests {
vault_path: &root,
profile: None,
};
let items = context_list(&params, None, &[], 20).unwrap();
let items = context_list(&params, None, &[], None, 20).unwrap();
assert_eq!(items.len(), 2);
}

Expand All @@ -755,7 +758,7 @@ mod tests {
vault_path: &root,
profile: None,
};
let items = context_list(&params, None, &["rust".into()], 20).unwrap();
let items = context_list(&params, None, &["rust".into()], None, 20).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0].path, "note.md");
}
Expand Down Expand Up @@ -803,10 +806,17 @@ mod tests {

let store = Store::open_memory().unwrap();
let f1 = store
.insert_file("People/John.md", "h1", 100, &["person".into()], "aaa111")
.insert_file(
"People/John.md",
"h1",
100,
&["person".into()],
"aaa111",
None,
)
.unwrap();
let f2 = store
.insert_file("daily.md", "h2", 100, &[], "bbb222")
.insert_file("daily.md", "h2", 100, &[], "bbb222", None)
.unwrap();
store.insert_edge(f2, f1, "mention").unwrap();
store
Expand Down Expand Up @@ -865,10 +875,11 @@ mod tests {
100,
&["project".into()],
"aaa111",
None,
)
.unwrap();
let f2 = store
.insert_file("01-Projects/child.md", "h2", 100, &[], "bbb222")
.insert_file("01-Projects/child.md", "h2", 100, &[], "bbb222", None)
.unwrap();
store.insert_edge(f2, f1, "wikilink").unwrap();
store.insert_edge(f1, f2, "wikilink").unwrap();
Expand Down Expand Up @@ -915,7 +926,7 @@ mod tests {

let store = Store::open_memory().unwrap();
store
.insert_file("result.md", "h1", 100, &["topic".into()], "aaa111")
.insert_file("result.md", "h1", 100, &["topic".into()], "aaa111", None)
.unwrap();

let params = ContextParams {
Expand Down Expand Up @@ -948,7 +959,7 @@ mod tests {

let store = Store::open_memory().unwrap();
store
.insert_file("long.md", "h1", 100, &[], "aaa111")
.insert_file("long.md", "h1", 100, &[], "aaa111", None)
.unwrap();

let params = ContextParams {
Expand Down Expand Up @@ -981,10 +992,10 @@ mod tests {

let store = Store::open_memory().unwrap();
let f1 = store
.insert_file("main.md", "h1", 100, &[], "aaa111")
.insert_file("main.md", "h1", 100, &[], "aaa111", None)
.unwrap();
let f2 = store
.insert_file("related.md", "h2", 100, &[], "bbb222")
.insert_file("related.md", "h2", 100, &[], "bbb222", None)
.unwrap();
store.insert_edge(f1, f2, "wikilink").unwrap();

Expand Down
30 changes: 27 additions & 3 deletions src/fts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod tests {
100,
&[],
&generate_docid("notes/ticket.md"),
None,
)
.unwrap();

Expand Down Expand Up @@ -54,6 +55,7 @@ mod tests {
100,
&[],
&generate_docid("notes/note.md"),
None,
)
.unwrap();

Expand All @@ -70,13 +72,34 @@ mod tests {
let store = setup_store();

let file_id1 = store
.insert_file("notes/a.md", "h1", 100, &[], &generate_docid("notes/a.md"))
.insert_file(
"notes/a.md",
"h1",
100,
&[],
&generate_docid("notes/a.md"),
None,
)
.unwrap();
let file_id2 = store
.insert_file("notes/b.md", "h2", 100, &[], &generate_docid("notes/b.md"))
.insert_file(
"notes/b.md",
"h2",
100,
&[],
&generate_docid("notes/b.md"),
None,
)
.unwrap();
let file_id3 = store
.insert_file("notes/c.md", "h3", 100, &[], &generate_docid("notes/c.md"))
.insert_file(
"notes/c.md",
"h3",
100,
&[],
&generate_docid("notes/c.md"),
None,
)
.unwrap();

// Chunk with "delivery" appearing multiple times should rank higher.
Expand Down Expand Up @@ -114,6 +137,7 @@ mod tests {
100,
&[],
&generate_docid("notes/del.md"),
None,
)
.unwrap();

Expand Down
26 changes: 20 additions & 6 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ mod tests {
100,
&["rust".into()],
&generate_docid("seed.md"),
None,
)
.unwrap();
let f2 = store
Expand All @@ -226,6 +227,7 @@ mod tests {
100,
&["rust".into()],
&generate_docid("linked.md"),
None,
)
.unwrap();
let _f3 = store
Expand All @@ -235,6 +237,7 @@ mod tests {
100,
&[],
&generate_docid("unlinked.md"),
None,
)
.unwrap();

Expand Down Expand Up @@ -265,10 +268,10 @@ mod tests {
fn test_graph_expand_skips_seeds() {
let store = Store::open_memory().unwrap();
let f1 = store
.insert_file("a.md", "h1", 100, &[], &generate_docid("a.md"))
.insert_file("a.md", "h1", 100, &[], &generate_docid("a.md"), None)
.unwrap();
let f2 = store
.insert_file("b.md", "h2", 100, &[], &generate_docid("b.md"))
.insert_file("b.md", "h2", 100, &[], &generate_docid("b.md"), None)
.unwrap();

store.insert_edge(f1, f2, "wikilink").unwrap();
Expand Down Expand Up @@ -302,13 +305,20 @@ mod tests {
fn test_graph_expand_multi_parent_takes_highest() {
let store = Store::open_memory().unwrap();
let f1 = store
.insert_file("a.md", "h1", 100, &[], &generate_docid("a.md"))
.insert_file("a.md", "h1", 100, &[], &generate_docid("a.md"), None)
.unwrap();
let f2 = store
.insert_file("b.md", "h2", 100, &[], &generate_docid("b.md"))
.insert_file("b.md", "h2", 100, &[], &generate_docid("b.md"), None)
.unwrap();
let f3 = store
.insert_file("shared.md", "h3", 100, &[], &generate_docid("shared.md"))
.insert_file(
"shared.md",
"h3",
100,
&[],
&generate_docid("shared.md"),
None,
)
.unwrap();

store.insert_edge(f1, f3, "wikilink").unwrap();
Expand Down Expand Up @@ -349,7 +359,9 @@ mod tests {
#[test]
fn test_graph_expand_empty_graph() {
let store = Store::open_memory().unwrap();
let f1 = store.insert_file("a.md", "h1", 100, &[], "aaa111").unwrap();
let f1 = store
.insert_file("a.md", "h1", 100, &[], "aaa111", None)
.unwrap();

let seeds = vec![RankedResult {
file_path: "a.md".into(),
Expand All @@ -374,6 +386,7 @@ mod tests {
100,
&["rust".into(), "cli".into()],
&generate_docid("seed.md"),
None,
)
.unwrap();
let f2 = store
Expand All @@ -383,6 +396,7 @@ mod tests {
100,
&["rust".into()],
&generate_docid("linked.md"),
None,
)
.unwrap();

Expand Down
Loading
Loading