Skip to content
Open
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ When working on this project, load the relevant skill(s) BEFORE writing any code
| `engram-ui-elements` | Adding or changing dashboard UI components or connected browsing flows. | [`skills/ui-elements/SKILL.md`](skills/ui-elements/SKILL.md) |
| `engram-visual-language` | Any dashboard styling, typography, spacing, or visual identity change. | [`skills/visual-language/SKILL.md`](skills/visual-language/SKILL.md) |
| `engram-backlog-triage` | Auditing open issues or PRs, triaging the backlog, or reviewing contributor submissions as a maintainer. | [`skills/backlog-triage/SKILL.md`](skills/backlog-triage/SKILL.md) |
| `engram-knowledge-recall` | Any context retrieval, recall request, session start, or working on a topic with no prior context. Requires Engram + an external knowledge-base MCP (Obsidian, Notion, etc.). | [`skills/knowledge-recall/SKILL.md`](skills/knowledge-recall/SKILL.md) |
| `gentleman-bubbletea` | When editing Go files in installer/internal/tui/, working on TUI screens, or adding new UI features. | [`skills/gentleman-bubbletea/SKILL.md`](skills/gentleman-bubbletea/SKILL.md) |
39 changes: 33 additions & 6 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This is the complete technical reference for Engram. For getting started, see th
|---------|-----------------|
| [Database Schema](#database-schema) | Tables, FTS5, SQLite config |
| [HTTP API](#http-api-endpoints) | All REST endpoints with request/response details |
| [MCP Tools](#mcp-tools-15-tools) | Detailed reference for all 15 memory tools |
| [MCP Tools](#mcp-tools-16-tools) | Detailed reference for all 16 memory tools |
| [Memory Protocol](#memory-protocol) | When/how agents should use the tools |
| [Project Name Normalization](#project-name-normalization) | Auto-detection, normalization, similar-project warnings |
| [Features](#features) | FTS5 search, timeline, privacy, git sync, compression |
Expand Down Expand Up @@ -67,6 +67,7 @@ All endpoints return JSON. Server listens on `127.0.0.1:7437`.
- `POST /sessions` — Create session. Body: `{id, project, directory}`
- `POST /sessions/{id}/end` — End session. Body: `{summary}`
- `GET /sessions/recent` — Recent sessions. Query: `?project=X&limit=N`
- `DELETE /sessions/{id}` — Delete empty session. Returns 409 if session has observations.

### Observations

Expand All @@ -78,7 +79,7 @@ All endpoints return JSON. Server listens on `127.0.0.1:7437`.

### Search

- `GET /search` — FTS5 search. Query: `?q=QUERY&type=TYPE&project=PROJECT&scope=SCOPE&limit=N`
- `GET /search` — FTS5 search. Query: `?q=QUERY&type=TYPE&project=PROJECT&scope=SCOPE&limit=N&since=DATE&until=DATE`

### Timeline

Expand All @@ -89,6 +90,7 @@ All endpoints return JSON. Server listens on `127.0.0.1:7437`.
- `POST /prompts` — Save user prompt. Body: `{session_id, content, project?}`
- `GET /prompts/recent` — Recent prompts. Query: `?project=X&limit=N`
- `GET /prompts/search` — Search prompts. Query: `?q=QUERY&project=X&limit=N`
- `DELETE /prompts/{id}` — Delete a prompt by ID. Returns 404 if not found.

### Context

Expand Down Expand Up @@ -125,12 +127,18 @@ All endpoints return JSON. Server listens on `127.0.0.1:7437`.

---

## MCP Tools (15 tools)
## MCP Tools (16 tools)

### mem_search

Search persistent memory across all sessions. Supports FTS5 full-text search with type/project/scope/limit filters.

- **query** (required): Search query — natural language or keywords. Empty queries are rejected.
- **since**: Filter observations created on or after this date (YYYY-MM-DD or RFC3339).
- **until**: Filter observations created on or before this date (YYYY-MM-DD or RFC3339).

Invalid date formats return an explicit error (SQLite's `datetime()` silently ignores bad input, so validation happens in Go).

### mem_save

Save structured observations. The tool description teaches agents the format:
Expand Down Expand Up @@ -164,6 +172,9 @@ Save user prompts — records what the user asked so future sessions have contex

Get recent memory context from previous sessions — shows sessions, prompts, and observations, with optional scope filtering for observations.

- **since**: Filter to sessions/observations on or after this date (YYYY-MM-DD or RFC3339).
- **until**: Filter to sessions/observations on or before this date (YYYY-MM-DD or RFC3339).

### mem_stats

Show memory system statistics — sessions, observations, prompts, projects.
Expand Down Expand Up @@ -200,6 +211,15 @@ Mark a session as completed with optional summary.

Extract structured learnings from text output. Looks for `## Key Learnings:` sections and saves each numbered/bulleted item as a separate observation. Duplicates are automatically skipped.

### mem_sessions

List sessions within a date range. Shows session date, project, and observation count.

- **since**: Filter sessions started on or after this date (YYYY-MM-DD or RFC3339).
- **until**: Filter sessions started on or before this date (YYYY-MM-DD or RFC3339).
- **project**: Filter by project name (optional).
- **limit**: Max results (default: 20, max: 50).

### mem_merge_projects

**Admin tool.** Merge multiple project name variants into a single canonical name. Accepts an array of source project names and a target canonical name. All observations, sessions, and prompts from the source projects are reassigned to the canonical project.
Expand Down Expand Up @@ -331,11 +351,18 @@ Use `engram projects consolidate` to interactively merge variant project names,

## Features

### Input Validation

- **Empty queries**: `Search()` rejects blank/whitespace-only queries before they reach FTS5 (which would crash on `MATCH ""`).
- **Date filters**: `since` and `until` accept only `YYYY-MM-DD` or RFC3339 formats. Invalid strings (e.g. `"yesterday"`, `"hace 3 dias"`) return an explicit error instead of being silently ignored by SQLite's `datetime()`.
- **Required fields**: `mem_save` validates that `title` and `content` are non-empty after trimming whitespace.
- **Import normalization**: Project names from imported JSON/sync data are normalized (lowercase, trimmed, collapsed hyphens) to match local conventions.

### Full-Text Search (FTS5)

- Searches across title, content, tool_name, type, and project
- Query sanitization: wraps each word in quotes to avoid FTS5 syntax errors
- Supports type and project filters
- Searches across title, content, tool_name, type, project, and topic_key
- Query sanitization: wraps each word in quotes to avoid FTS5 syntax errors on special characters (`()`, `*`, `+`, `"`)
- Supports type, project, scope, since, and until filters

### Timeline (Progressive Disclosure)

Expand Down
14 changes: 12 additions & 2 deletions cmd/engram/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ var (
storeTimeline = func(s *store.Store, observationID int64, before, after int) (*store.TimelineResult, error) {
return s.Timeline(observationID, before, after)
}
storeFormatContext = func(s *store.Store, project, scope string) (string, error) { return s.FormatContext(project, scope) }
storeFormatContext = func(s *store.Store, project, scope string) (string, error) { return s.FormatContext(project, scope, "", "") }
storeStats = func(s *store.Store) (*store.Stats, error) { return s.Stats() }
storeExport = func(s *store.Store) (*store.ExportData, error) { return s.Export() }
jsonMarshalIndent = json.MarshalIndent
Expand Down Expand Up @@ -287,7 +287,7 @@ func cmdTUI(cfg store.Config) {

func cmdSearch(cfg store.Config) {
if len(os.Args) < 3 {
fmt.Fprintln(os.Stderr, "usage: engram search <query> [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N]")
fmt.Fprintln(os.Stderr, "usage: engram search <query> [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N] [--since DATE] [--until DATE]")
exitFunc(1)
}

Expand Down Expand Up @@ -319,6 +319,16 @@ func cmdSearch(cfg store.Config) {
opts.Scope = os.Args[i+1]
i++
}
case "--since":
if i+1 < len(os.Args) {
opts.Since = os.Args[i+1]
i++
}
case "--until":
if i+1 < len(os.Args) {
opts.Until = os.Args[i+1]
i++
}
default:
queryParts = append(queryParts, os.Args[i])
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/engram/main_extra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func stubRuntimeHooks(t *testing.T) {
return s.Timeline(observationID, before, after)
}
storeFormatContext = func(s *store.Store, project, scope string) (string, error) {
return s.FormatContext(project, scope)
return s.FormatContext(project, scope, "", "")
}
storeStats = func(s *store.Store) (*store.Stats, error) { return s.Stats() }
storeExport = func(s *store.Store) (*store.ExportData, error) { return s.Export() }
Expand Down Expand Up @@ -850,6 +850,8 @@ func TestCommandErrorSeamsAndUncoveredBranches(t *testing.T) {
storeFormatContext = func(*store.Store, string, string) (string, error) {
return "", errors.New("forced context error")
}
// Note: signature stays (store, project, scope) because storeFormatContext is a wrapper
// that calls FormatContext(project, scope, "", "") internally
_, stderr, recovered := captureOutputAndRecover(t, func() { cmdContext(cfg) })
assertFatal(t, stderr, recovered, "forced context error")
})
Expand Down
Loading