Skip to content

Commit 10336a3

Browse files
committed
docs: v1.6.0 changelog, README, CLAUDE.md updates
Update CHANGELOG with v1.6.0 Onboarding + Identity entry, bump README tool/endpoint counts to 25 MCP tools and 26 HTTP endpoints, add identity endpoints to the HTTP table, check off Identity roadmap item, and document identity.rs and onboarding.rs modules in CLAUDE.md architecture section. Fix three clippy warnings (collapsible_if x2, if_same_then_else x1) and reformat source files changed by this task.
1 parent 6759d78 commit 10336a3

10 files changed

Lines changed: 146 additions & 122 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## v1.6.0 — Onboarding + Identity (2026-04-10)
4+
5+
### Added
6+
- **Interactive onboarding** (`engraph init`) — polished CLI with welcome banner, vault scan checkmarks, identity prompts via dialoguer, progress bars, actionable next steps
7+
- **Agent onboarding**`engraph init --detect --json` for vault inspection, `--json` for non-interactive apply. Two-phase detect → apply flow for AI agents.
8+
- **`identity` MCP tool + CLI + HTTP** — returns compact L0/L1 identity block (~170 tokens) for AI session context
9+
- **`setup` MCP tool + HTTP** — first-time setup from inside an MCP session (detect/apply modes)
10+
- **`identity_facts` table** — SQLite storage for L0 (static identity) and L1 (dynamic context) facts
11+
- **L1 auto-extraction** — active projects, key people, current focus, OOO status, blocking items extracted during `engraph index`
12+
- **`engraph identity --refresh`** — re-extract L1 facts without full reindex
13+
- **`[identity]` config section** — name, role, vault_purpose in config.toml
14+
- **`[memory]` config section** — feature flags for identity/timeline/mining
15+
16+
### Changed
17+
- MCP tools: 23 → 25
18+
- HTTP endpoints: 24 → 26
19+
- Dependencies: +dialoguer 0.12, +console 0.16, +regex 1
20+
321
## v1.5.5 — Housekeeping (2026-04-10)
422

523
### Added

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Single binary with 26 modules behind a lib crate:
3232
- `indexer.rs` — orchestrates vault walking (via `ignore` crate for `.gitignore` support), diffing, chunking, embedding, writes to store + sqlite-vec + FTS5, vault graph edge building (wikilinks + people detection), and folder centroid computation. Exposes `index_file`, `remove_file`, `rename_file` as public per-file functions. `run_index_shared` accepts external store/embedder for watcher FullRescan. Dimension migration on model change.
3333
- `temporal.rs` — temporal search lane. Extracts note dates from frontmatter `date:` field or `YYYY-MM-DD` filename patterns. Heuristic date parsing for natural language ("today", "yesterday", "last week", "this month", "recent", month names, ISO dates, date ranges). Smooth decay scoring for files near but outside target date range. Provides `extract_note_date()` for indexing and `score_temporal()` + `parse_date_range_heuristic()` for search
3434
- `search.rs` — hybrid search orchestrator. `search_with_intelligence()` runs the full pipeline: orchestrate (intent + expansions) → 5-lane RRF retrieval (semantic + FTS5 + graph + reranker + temporal) per expansion → two-pass RRF fusion. `search_internal()` is a thin wrapper without intelligence models. Adaptive lane weights per query intent including temporal (1.5 weight for time-aware queries). Results display normalized confidence percentages (0-100%) instead of raw RRF scores.
35+
- `identity.rs` — L1 extraction engine: active projects, key people, current focus, OOO, blocking. `format_identity_block()` for compact session context. `extract_l1_facts()` called after indexing.
36+
- `onboarding.rs` — Interactive CLI UX: welcome banner, vault scan, identity prompts (dialoguer), agent mode (--detect --json, --json). `run_interactive()`, `run_detect_json()`, `run_apply_json()`.
3537

3638
`main.rs` is a thin clap CLI (async via `#[tokio::main]`). Subcommands: `index` (with progress bar), `search` (with `--explain`, loads intelligence models when enabled), `status` (shows intelligence state + date coverage stats), `clear`, `init` (intelligence onboarding prompt, detects Obsidian CLI + AI agents), `configure` (`--enable-intelligence`, `--disable-intelligence`, `--model`, `--obsidian-cli`, `--no-obsidian-cli`, `--agent`, `--add-api-key`, `--list-api-keys`, `--revoke-api-key`, `--setup-chatgpt`), `models`, `graph` (show/stats), `context` (read/list/vault-map/who/project/topic), `write` (create/append/update-metadata/move/edit/rewrite/edit-frontmatter/delete), `migrate` (para with `--preview`/`--apply`/`--undo` for PARA vault restructuring), `serve` (MCP stdio server with file watcher + intelligence + optional `--http`/`--port`/`--host`/`--no-auth` for HTTP REST API).
3739

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ engraph turns your markdown vault into a searchable knowledge graph that any AI
2121
Plain vector search treats your notes as isolated documents. But knowledge isn't flat — your notes link to each other, share tags, reference the same people and projects. engraph understands these connections.
2222

2323
- **5-lane hybrid search** — semantic embeddings + BM25 full-text + graph expansion + cross-encoder reranking + temporal scoring, fused via [Reciprocal Rank Fusion](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf). An LLM orchestrator classifies queries and adapts lane weights per intent. Time-aware queries like "what happened last week" or "March 2026 notes" activate the temporal lane automatically.
24-
- **MCP server for AI agents**`engraph serve` exposes 22 tools (search, read, section-level editing, frontmatter mutations, vault health, context bundles, note creation, PARA migration) that Claude, Cursor, or any MCP client can call directly.
25-
- **HTTP REST API**`engraph serve --http` adds an axum-based HTTP server alongside MCP with 23 REST endpoints, API key authentication, rate limiting, and CORS. Web-based agents and scripts can query your vault with simple `curl` calls.
24+
- **MCP server for AI agents**`engraph serve` exposes 25 tools (search, read, section-level editing, frontmatter mutations, vault health, context bundles, note creation, PARA migration, identity) that Claude, Cursor, or any MCP client can call directly.
25+
- **HTTP REST API**`engraph serve --http` adds an axum-based HTTP server alongside MCP with 26 REST endpoints, API key authentication, rate limiting, and CORS. Web-based agents and scripts can query your vault with simple `curl` calls.
2626
- **Section-level editing** — AI agents can read, replace, prepend, or append to specific sections by heading. Full note rewriting with frontmatter preservation. Granular frontmatter mutations (set/remove fields, add/remove tags and aliases).
2727
- **Vault health diagnostics** — detect orphan notes, broken wikilinks, stale content, and tag hygiene issues. Available as MCP tool and CLI command.
2828
- **Obsidian CLI integration** — auto-detects running Obsidian and delegates compatible operations. Circuit breaker (Closed/Degraded/Open) ensures graceful fallback.
@@ -61,7 +61,7 @@ Your vault (markdown files)
6161
│ Search: Orchestrator → 4-lane retrieval │
6262
│ → Reranker → Two-pass RRF fusion │
6363
│ │
64-
22 MCP tools + 23 REST endpoints │
64+
25 MCP tools + 26 REST endpoints │
6565
└─────────────────────────────────────────────┘
6666
6767
@@ -268,7 +268,7 @@ Returns orphan notes (no links in or out), broken wikilinks, stale notes, and ta
268268

269269
`engraph serve --http` adds a full REST API alongside the MCP server, exposing the same capabilities over HTTP for web agents, scripts, and integrations.
270270

271-
**24 endpoints:**
271+
**26 endpoints:**
272272

273273
| Method | Endpoint | Permission | Description |
274274
|--------|----------|------------|-------------|
@@ -292,6 +292,8 @@ Returns orphan notes (no links in or out), broken wikilinks, stale notes, and ta
292292
| POST | `/api/unarchive` | write | Restore archived note |
293293
| POST | `/api/update-metadata` | write | Update note metadata |
294294
| POST | `/api/delete` | write | Delete note (soft or hard) |
295+
| GET | `/api/identity` | read | User identity (L0) and current context (L1) |
296+
| POST | `/api/setup` | write | First-time onboarding setup (detect/apply modes) |
295297
| POST | `/api/reindex-file` | write | Re-index a single file after external edits |
296298
| POST | `/api/migrate/preview` | write | Preview PARA migration (classify + suggest moves) |
297299
| POST | `/api/migrate/apply` | write | Apply PARA migration (move files) |
@@ -526,7 +528,7 @@ STYLE:
526528
| Search method | 5-lane RRF (semantic + BM25 + graph + reranker + temporal) | Vector similarity only | Keyword only |
527529
| Query understanding | LLM orchestrator classifies intent, adapts weights | None | None |
528530
| Understands note links | Yes (wikilink graph traversal) | No | Limited (backlinks panel) |
529-
| AI agent access | MCP server (22 tools) + HTTP REST API (23 endpoints) | Custom API needed | No |
531+
| AI agent access | MCP server (25 tools) + HTTP REST API (26 endpoints) | Custom API needed | No |
530532
| Write capability | Create/edit/rewrite/delete with smart filing | No | Manual |
531533
| Vault health | Orphans, broken links, stale notes, tag hygiene | No | Limited |
532534
| Real-time sync | File watcher, 2s debounce | Manual re-index | N/A |
@@ -543,8 +545,9 @@ engraph is not a replacement for Obsidian — it's the intelligence layer that s
543545
- LLM research orchestrator: query intent classification + query expansion + adaptive lane weights
544546
- llama.cpp inference via Rust bindings (GGUF models, Metal GPU on macOS, CUDA on Linux)
545547
- Intelligence opt-in: heuristic fallback when disabled, LLM-powered when enabled
546-
- MCP server with 23 tools (8 read, 10 write, 1 index, 1 diagnostic, 3 migrate) via stdio
547-
- HTTP REST API with 24 endpoints, API key auth (`eg_` prefix), rate limiting, CORS — enabled via `engraph serve --http`
548+
- MCP server with 25 tools (8 read, 10 write, 2 identity, 1 index, 1 diagnostic, 3 migrate) via stdio
549+
- HTTP REST API with 26 endpoints, API key auth (`eg_` prefix), rate limiting, CORS — enabled via `engraph serve --http`
550+
- User identity with L0/L1 tiered context for AI agent session starts
548551
- Section-level reading and editing: target specific headings with replace/prepend/append modes
549552
- Full note rewriting with automatic frontmatter preservation
550553
- Granular frontmatter mutations: set/remove fields, add/remove tags and aliases
@@ -573,7 +576,7 @@ engraph is not a replacement for Obsidian — it's the intelligence layer that s
573576
- [x] ~~HTTP/REST API — complement MCP with a standard web API~~ (v1.3)
574577
- [x] ~~PARA migration — AI-assisted vault restructuring with preview/apply/undo~~ (v1.4)
575578
- [x] ~~ChatGPT Actions — OpenAPI 3.1.0 spec + plugin manifest + `--setup-chatgpt` helper~~ (v1.5)
576-
- [ ] Identity — user context at session start, enhanced onboarding (v1.6)
579+
- [x] ~~Identity — user context at session start, enhanced onboarding~~ (v1.6)
577580
- [ ] Timeline — temporal knowledge graph with point-in-time queries (v1.7)
578581
- [ ] Mining — automatic fact extraction from vault notes (v1.8)
579582

src/http.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,13 +1116,9 @@ async fn handle_setup(
11161116
identity_only: false,
11171117
reindex_only: false,
11181118
};
1119-
let result = crate::onboarding::run_apply_json(
1120-
&state.vault_path,
1121-
&mut config,
1122-
&data_dir,
1123-
flags,
1124-
)
1125-
.map_err(|e| ApiError::internal(&format!("{e:#}")))?;
1119+
let result =
1120+
crate::onboarding::run_apply_json(&state.vault_path, &mut config, &data_dir, flags)
1121+
.map_err(|e| ApiError::internal(&format!("{e:#}")))?;
11261122
Ok(Json(result))
11271123
}
11281124
other => Err(ApiError::bad_request(&format!(

src/identity.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,18 @@ pub fn extract_l1_facts(store: &Store, profile: &VaultProfile) -> Result<L1Summa
7171
daily_files.sort_by(|a, b| b.note_date.cmp(&a.note_date));
7272

7373
// ── Current focus (most recent daily note) ──────────────
74-
if let Some(latest) = daily_files.first() {
75-
if let Ok(chunks) = store.get_chunks_by_file(latest.id) {
76-
let focus_re =
77-
Regex::new(r"(?i)morning\s+focus|top\s+priorit|priorities").unwrap();
78-
for chunk in &chunks {
79-
if focus_re.is_match(&chunk.heading) {
80-
let items = extract_bullet_items(&chunk.snippet, 3);
81-
for item in items {
82-
store.upsert_identity_fact(1, "current_focus", &item, None)?;
83-
summary.current_focus += 1;
84-
}
85-
break;
74+
if let Some(latest) = daily_files.first()
75+
&& let Ok(chunks) = store.get_chunks_by_file(latest.id)
76+
{
77+
let focus_re = Regex::new(r"(?i)morning\s+focus|top\s+priorit|priorities").unwrap();
78+
for chunk in &chunks {
79+
if focus_re.is_match(&chunk.heading) {
80+
let items = extract_bullet_items(&chunk.snippet, 3);
81+
for item in items {
82+
store.upsert_identity_fact(1, "current_focus", &item, None)?;
83+
summary.current_focus += 1;
8684
}
85+
break;
8786
}
8887
}
8988
}
@@ -171,7 +170,10 @@ pub fn format_identity_block(config: &Config, store: &Store) -> Result<String> {
171170
.max()
172171
.unwrap_or("unknown");
173172

174-
out.push_str(&format!("\n## Current State (L1) [updated {}]\n", latest_ts));
173+
out.push_str(&format!(
174+
"\n## Current State (L1) [updated {}]\n",
175+
latest_ts
176+
));
175177

176178
// Group facts by key.
177179
let project_vals: Vec<&str> = facts
@@ -343,13 +345,28 @@ mod tests {
343345

344346
// Insert L1 facts manually.
345347
store
346-
.upsert_identity_fact(1, "active_project", "ProjectA", Some("01-Projects/ProjectA.md"))
348+
.upsert_identity_fact(
349+
1,
350+
"active_project",
351+
"ProjectA",
352+
Some("01-Projects/ProjectA.md"),
353+
)
347354
.unwrap();
348355
store
349-
.upsert_identity_fact(1, "active_project", "ProjectB", Some("01-Projects/ProjectB.md"))
356+
.upsert_identity_fact(
357+
1,
358+
"active_project",
359+
"ProjectB",
360+
Some("01-Projects/ProjectB.md"),
361+
)
350362
.unwrap();
351363
store
352-
.upsert_identity_fact(1, "key_person", "Alice", Some("03-Resources/People/Alice.md"))
364+
.upsert_identity_fact(
365+
1,
366+
"key_person",
367+
"Alice",
368+
Some("03-Resources/People/Alice.md"),
369+
)
353370
.unwrap();
354371
store
355372
.upsert_identity_fact(1, "current_focus", "Ship feature X", None)

src/indexer.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,14 @@ pub fn run_index(vault_path: &Path, config: &Config, rebuild: bool) -> Result<In
461461
store.set_meta("embedding_dim", &model_dim.to_string())?;
462462

463463
let profile = crate::config::Config::load_vault_profile().ok().flatten();
464-
run_index_inner(vault_path, config, &store, &mut embedder, rebuild, profile.as_ref())
464+
run_index_inner(
465+
vault_path,
466+
config,
467+
&store,
468+
&mut embedder,
469+
rebuild,
470+
profile.as_ref(),
471+
)
465472
}
466473

467474
/// Like [`run_index`], but accepts shared `Store` and `Embedder` references.
@@ -668,10 +675,10 @@ fn run_index_inner(
668675
}
669676

670677
// Extract L1 identity facts from the freshly indexed vault
671-
if let Some(p) = profile {
672-
if let Err(e) = crate::identity::extract_l1_facts(store, p) {
673-
tracing::warn!("L1 identity extraction failed (non-fatal): {e:#}");
674-
}
678+
if let Some(p) = profile
679+
&& let Err(e) = crate::identity::extract_l1_facts(store, p)
680+
{
681+
tracing::warn!("L1 identity extraction failed (non-fatal): {e:#}");
675682
}
676683

677684
let duration = start.elapsed();

src/main.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,17 @@ async fn main() -> Result<()> {
548548
}
549549
}
550550

551-
Command::Init { path, identity, reindex, detect, json, quiet, name, role, purpose } => {
551+
Command::Init {
552+
path,
553+
identity,
554+
reindex,
555+
detect,
556+
json,
557+
quiet,
558+
name,
559+
role,
560+
purpose,
561+
} => {
552562
cfg.merge_vault_path(path);
553563
let vault_path = match &cfg.vault_path {
554564
Some(p) => p.clone(),
@@ -558,27 +568,28 @@ async fn main() -> Result<()> {
558568

559569
if detect {
560570
let result = engraph::onboarding::run_detect_json(&vault_path)?;
561-
if json {
562-
println!("{}", serde_json::to_string_pretty(&result)?);
563-
} else {
564-
println!("{}", serde_json::to_string_pretty(&result)?);
565-
}
571+
println!("{}", serde_json::to_string_pretty(&result)?);
566572
return Ok(());
567573
}
568574

569575
if json {
570576
let flags = engraph::onboarding::ApplyFlags {
571-
name, role, purpose,
577+
name,
578+
role,
579+
purpose,
572580
identity_only: identity,
573581
reindex_only: reindex,
574582
};
575-
let result = engraph::onboarding::run_apply_json(&vault_path, &mut cfg, &data_dir, flags)?;
583+
let result =
584+
engraph::onboarding::run_apply_json(&vault_path, &mut cfg, &data_dir, flags)?;
576585
println!("{}", serde_json::to_string_pretty(&result)?);
577586
return Ok(());
578587
}
579588

580589
let flags = engraph::onboarding::InteractiveFlags {
581-
name, role, purpose,
590+
name,
591+
role,
592+
purpose,
582593
identity_only: identity,
583594
reindex_only: reindex,
584595
quiet,

0 commit comments

Comments
 (0)