Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ me login
# Store a memory
me memory create "Auth uses bcrypt with cost 12" --tree design.auth

# Search by meaning
# Search by meaning + keywords
me memory search "how does authentication work"

# Connect to your AI tools (Claude Code, Gemini, Codex, OpenCode)
Expand Down
6 changes: 3 additions & 3 deletions docs/cli/me-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ me memory search [query] [options]

| Argument | Required | Description |
|----------|----------|-------------|
| `query` | no | Semantic search query (shorthand for `--semantic`). |
| `query` | no | Hybrid search query (uses both semantic and fulltext search). |

| Option | Description |
|--------|-------------|
Expand All @@ -89,12 +89,12 @@ me memory search [query] [options]
| `--weight-fulltext <w>` | Fulltext weight, 0-1. |
| `--order-by <dir>` | Sort direction: `asc` or `desc`. |

At least one search criterion is required. When both `--semantic` and `--fulltext` are provided, results are ranked using Reciprocal Rank Fusion (hybrid mode).
At least one search criterion is required. A positional `query` runs hybrid search by sending the same text to semantic and fulltext ranking. Use `--semantic` for pure vector search, `--fulltext` for pure keyword search, or both flags to provide different text for each mode.

### Examples

```bash
# Semantic search
# Hybrid search (recommended default)
me memory search "how does authentication work"

# Keyword search
Expand Down
8 changes: 5 additions & 3 deletions docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ Memory Engine supports three search modes. Quick guide:
Find memories by meaning. Uses vector embeddings and cosine similarity.

```bash
me memory search "how does authentication work"
me memory search --semantic "how does authentication work"
```

Good for finding conceptually related content even when the exact words differ.
Good for finding conceptually related content even when the exact words differ. For short literal terms, identifiers, and exact words, prefer fulltext or hybrid search; semantic-only rankings are not lexical and can return unrelated short memories.

### Fulltext search

Expand All @@ -189,10 +189,12 @@ Good for finding memories with specific terms, names, or identifiers.
Combine both modes. Results are ranked using Reciprocal Rank Fusion (RRF), which merges the two ranked lists into a single result set.

```bash
me memory search "embedding performance"
# or provide different text for each ranker:
me memory search --semantic "embedding performance" --fulltext "nomic ollama"
```

Good when you want both meaning-based and keyword-based relevance.
Good when you want both meaning-based and keyword-based relevance. The positional CLI query uses hybrid search and is the recommended default.

### Filters

Expand Down
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ me memory create "PostgreSQL 18 supports native UUIDv7 generation." \
## Search

```bash
# Semantic search (by meaning)
# Hybrid search (recommended default: meaning + keywords)
me memory search "UUID generation in Postgres"

# Keyword search
me memory search --fulltext "UUIDv7"

# Hybrid (both combined)
me memory search --semantic "UUID generation" --fulltext "PostgreSQL 18"
# Pure semantic search (by meaning only)
me memory search --semantic "database-generated identifiers"
```

## Browse the tree
Expand Down
3 changes: 3 additions & 0 deletions docs/mcp-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ Search memory proactively:

## Search Examples

# Hybrid search (recommended: meaning + keywords)
me_memory_search({semantic: "database-generated identifiers", fulltext: "database-generated identifiers"})

# Semantic search (by meaning)
me_memory_search({semantic: "how does authentication work"})

Expand Down
17 changes: 13 additions & 4 deletions docs/mcp/me_memory_search.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Search and browse memories using text matching and/or filters.

Supports three search modes: **semantic** (meaning-based), **fulltext** (keyword-based via BM25), or **hybrid** (both combined via Reciprocal Rank Fusion). Combine any search mode with tree, meta, and temporal filters.
Supports three search modes: **semantic** (meaning-based), **fulltext** (keyword-based via BM25), or **hybrid** (both combined via Reciprocal Rank Fusion). Combine any search mode with tree, meta, and temporal filters. For ordinary user queries, short terms, identifiers, or exact words, prefer hybrid by setting both `semantic` and `fulltext` to the query text.

## Parameters

Expand Down Expand Up @@ -84,8 +84,17 @@ See [Tree filter syntax](../concepts.md#tree-filter-syntax) for the full referen
```json
{
"semantic": "how does authentication work",
"limit": 10,
"semanticThreshold": 0.7
"limit": 10
}
```

### Hybrid search (recommended default)

```json
{
"semantic": "panics",
"fulltext": "panics",
"limit": 10
}
```

Expand Down Expand Up @@ -115,6 +124,6 @@ See [Tree filter syntax](../concepts.md#tree-filter-syntax) for the full referen

- Provide at least one of `semantic`, `fulltext`, or a filter (`tree`, `meta`, `temporal`, `grep`) -- otherwise the search has no criteria.
- Optional parameters may be omitted or explicitly passed as `null` — both are treated as "no value".
- When both `semantic` and `fulltext` are provided, results are ranked using Reciprocal Rank Fusion (hybrid mode).
- When both `semantic` and `fulltext` are provided, results are ranked using Reciprocal Rank Fusion (hybrid mode). This is recommended for single-word or literal searches because semantic-only ranking does not guarantee exact-word matches rank first.
- `order_by` only applies to filter-only searches (no `semantic`/`fulltext`). Ranked searches are always sorted by score.
- `score` ranges from 0 to 1, where 1 is the best match.
22 changes: 9 additions & 13 deletions packages/cli/commands/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function createMemoryGetCommand(): Command {
function createMemorySearchCommand(): Command {
return new Command("search")
.description("search memories")
.argument("[query]", "semantic search query (shorthand for --semantic)")
.argument("[query]", "hybrid search query (semantic + fulltext)")
.option("--semantic <text>", "semantic (vector) search")
.option("--fulltext <text>", "BM25 keyword search")
.option(
Expand All @@ -234,24 +234,20 @@ function createMemorySearchCommand(): Command {
requireSession(creds, fmt);
requireEngine(creds, fmt);

// Resolve semantic query
const semantic = query ?? opts.semantic ?? null;
if (query && opts.semantic) {
// Resolve search text. A positional query runs hybrid search
if (query && opts.semantic && opts.fulltext) {
const error =
"Positional query is masked by --semantic and --fulltext flags.";
if (fmt === "text") {
clack.log.error(
"Cannot use both positional query and --semantic flag.",
);
clack.log.error(error);
} else {
output(
{ error: "Cannot use both positional query and --semantic" },
fmt,
() => {},
);
output({ error }, fmt, () => {});
}
process.exit(1);
}

const fulltext = opts.fulltext ?? null;
const semantic = opts.semantic ?? query ?? null;
const fulltext = opts.fulltext ?? query ?? null;
const tree = opts.tree ?? null;
const meta = opts.meta ? parseMeta(opts.meta) : null;

Expand Down
6 changes: 4 additions & 2 deletions packages/cli/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,17 @@ Docs: ${docUrl("me_memory_create")}`,
title: "Search Memories",
description: `Search and browse memories using text matching and/or filters.

Search modes: semantic (meaning), fulltext (keywords), or both (hybrid). Combine with tree, meta, and temporal filters. Results scored 0-1.
Search modes: semantic (meaning), fulltext (keywords), or both (hybrid). For ordinary queries, short terms, identifiers, or exact words, prefer hybrid by setting both semantic and fulltext to the query text. Combine with tree, meta, and temporal filters. Results scored 0-1.

Docs: ${docUrl("me_memory_search")}`,
inputSchema: {
semantic: z
.string()
.optional()
.nullable()
.describe("Natural language query for semantic/meaning search"),
.describe(
"Natural language query for semantic/meaning search. For short or literal queries, also set fulltext to the same value.",
),
fulltext: z
.string()
.optional()
Expand Down