Skip to content
Closed
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 .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "mempalace",
"source": "./.claude-plugin",
"description": "AI memory system — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, guided setup.",
"version": "3.3.0",
"version": "3.3.1",
"author": {
"name": "milla-jovovich"
}
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mempalace",
"version": "3.3.0",
"version": "3.3.1",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mempalace",
"version": "3.3.0",
"version": "3.3.1",
"description": "Give your AI a memory — mine projects and conversations into a searchable palace. 19 MCP tools, auto-save hooks, and guided setup.",
"author": {
"name": "milla-jovovich"
Expand Down
70 changes: 69 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,75 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

---

## [Unreleased] — v3.3.0 (on develop)
## [3.3.1] — 2026-04-16

### New Features

**Multi-language entity detection** — lexical patterns (person verbs, pronouns, dialogue markers, project verbs, stopwords, candidate character classes) now live in the optional `entity` section of each locale JSON under `mempalace/i18n/<lang>.json`. Every public function in `entity_detector` accepts a `languages=` tuple and unions patterns across enabled locales. Default stays `("en",)` so existing English-only callers are unchanged. (#911)

- **Five new fully-supported locales** with CLI strings, AAAK compression instructions, and entity-detection patterns:
- Brazilian Portuguese `pt-br` (#156)
- Russian `ru` (#760)
- Italian `it` (#907)
- Hindi `hi` (#773)
- Indonesian `id` (#778)
- **`MempalaceConfig.entity_languages`** — persistent palace-level language selection; `MEMPALACE_ENTITY_LANGUAGES` env override; `mempalace init --lang en,pt-br` flag that saves to `~/.mempalace/config.json` (#911)
- **Per-language `candidate_pattern`** — non-Latin scripts register their own character class, so names like `João`, `Инна`, `राज` are no longer silently dropped by the ASCII-only default (#911)
- **VSCode devcontainer** matching the CI environment (#881)
- `MEMPAL_VERBOSE` env toggle — developers see diaries surfaced in chat while the default remains silent (#871)
- `created_at` timestamps included in search results (#846)

### Bug Fixes

**i18n / Unicode**

- Script-aware word boundaries for combining-mark scripts — Python's `\b` fails on Devanagari vowel signs (`ा ी ु`), Arabic, Hebrew, Thai, Tamil, Khmer etc., truncating names like `अनीता` → `अनीत` and making person-verb patterns never fire. Locales now declare an optional `boundary_chars` field and the i18n loader expands `\b` into a script-aware lookaround boundary (#932)
- Case-insensitive BCP 47 language code resolution — `--lang PT-BR`, `zh-cn`, `Pt-Br` previously fell through to English silently; now resolve to the canonical locale file via lowercase matching, with the entity-pattern cache keyed on the canonical form so casing variations share one cache entry (#928)
- Wire i18n candidate patterns into `miner._extract_entities_for_metadata()`, `palace.build_closet_lines()`, and `entity_registry.extract_unknown_candidates()` — three code paths that still hardcoded ASCII-only `[A-Z][a-z]{2,}` and silently missed Cyrillic, accented Latin, and non-Latin entity metadata tags (#931)
- Explicit `encoding="utf-8"` on `Path.read_text()` calls across entity_registry, instructions_cli, split_mega_files, and onboarding tests — prevents Windows GBK (and other non-UTF-8) locales from corrupting UTF-8 files (#946, #776)
- `ko.json` `status_drawers` used `{drawers}` instead of `{count}`, showing the raw template string instead of the number (#758)
- Move `test_i18n.py` from inside the installed package into `tests/` so pytest actually collects it; remove the `sys.path.insert` hack (#758)
- `Dialect.from_config()` defaulted to `current_lang()` (module-global) when config had no `lang` key — replaced with explicit `"en"` fallback for determinism (#758)

**Other**

- Guard `KnowledgeGraph.close()` and `query_relationship`/`timeline`/`stats` methods with the instance lock to prevent concurrent-access corruption (#887, #884)
- Replace invalid `{"decision": "allow"}` with `{}` in hook responses — the string wasn't a valid decision value and triggered schema warnings (#885)
- `entity_registry.research()` defaults to local-only — previously made outbound Wikipedia HTTPS requests without explicit user opt-in; callers now must pass `allow_network=True` (#811)
- Precompact hook no longer blocks compaction when it fails or takes too long (#856, #858, #863)
- Redirect stdout to stderr during MCP server import so library logging can't corrupt the JSON-RPC channel (#225, #864)
- `mempalace init` auto-adds per-project files to `.gitignore` in git repositories so users don't accidentally commit `mempalace.yaml` / `entities.json` (#185, #866)
- Searcher guards against empty ChromaDB query results that previously raised on edge-case corpora (#195, #865)
- Return empty status instead of an error on a cold-start palace with no drawers yet (#830, #831)
- Restrict file permissions on sensitive palace data (#814)
- Slack transcript importer writes a provenance header and preserves speaker IDs (#815)
- Allow `mempalace mine` to run in directories without a local `mempalace.yaml` and surface the missing-yaml warning on stderr (#604)
- Security hook injection fix (#812)
- Save hook auto-mines transcripts even when `MEMPAL_DIR` is unset (#840)
- Pin the Pages custom domain via a shipped `CNAME` in the deploy artifact (#877)
- Version drift safeguard — sync pyproject + `version.py` + README badge in one place (#876)
- Deploy docs workflow now runs on `develop` only, preventing accidental main-branch deploys (#845)

### Improvements

- Regex compilation optimization for entity extraction — pre-compile per-entity pattern sets once and cache by `(name, languages)` tuple, so multi-language callers don't thrash the cache (#880)
- Knowledge-graph value sanitization now preserves natural punctuation (commas, colons, parentheses) that commonly appears in KG subject/object values (#873)

### Documentation

- Clarify that `mempalace init` requires a `<dir>` argument in CLI help text (#210, #862)
- Domain name and specific impostor sites called out in the scam-alert section (#869)
- Tightened `SECURITY.md` with a real version-support policy and the GHPVR-only reporting channel (#810)
- Fixed stale `pyproject.toml` URLs (#853)
- v4 planning prep (#852)

### Internal

- `palace_graph` tunnel helper test coverage (#908)

---

## [3.3.0] — 2026-04-13

### New Features
- Closet layer — a compact searchable index of pointers to verbatim drawers, enabling fast topical lookup without reading all content (#788)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
MIT — see [LICENSE](LICENSE).

<!-- Link Definitions -->
[version-shield]: https://img.shields.io/badge/version-3.3.0-4dc9f6?style=flat-square&labelColor=0a0e14
[version-shield]: https://img.shields.io/badge/version-3.3.1-4dc9f6?style=flat-square&labelColor=0a0e14
[release-link]: https://github.com/MemPalace/mempalace/releases
[python-shield]: https://img.shields.io/badge/python-3.9+-7dd8f8?style=flat-square&labelColor=0a0e14&logo=python&logoColor=7dd8f8
[python-link]: https://www.python.org/
Expand Down
194 changes: 194 additions & 0 deletions mempalace/i18n/he.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
{
"lang": "he",
"label": "עברית",
"terms": {
"palace": "ארמון",
"wing": "אגף",
"hall": "אולם",
"closet": "ארון",
"drawer": "מגירה",
"mine": "כרייה",
"search": "חיפוש",
"status": "סטטוס",
"init": "אתחול",
"repair": "תיקון",
"migrate": "העברה",
"entity": "ישות",
"topic": "נושא"
},
"cli": {
"mine_start": "כורה את {path}...",
"mine_complete": "הושלם. נוצרו {closets} ארונות ו-{drawers} מגירות.",
"mine_skip": "כבר נכרה. השתמש ב--force כדי לכרות מחדש.",
"search_no_results": "לא נמצאו תוצאות עבור: {query}",
"search_results": "נמצאו {count} תוצאות:",
"status_palace": "ארמון: {path}",
"status_wings": "{count} אגפים",
"status_closets": "{count} ארונות",
"status_drawers": "{count} מגירות",
"init_complete": "הארמון אותחל ב-{path}",
"init_exists": "כבר קיים ארמון ב-{path}",
"repair_complete": "התיקון הושלם. תוקנו {fixed} בעיות.",
"migrate_complete": "ההעברה הושלמה.",
"no_palace": "לא נמצא ארמון. הרץ: mempalace init <dir>"
},
"aaak": {
"instruction": "דחוס לפורמט אינדקס. השתמש במקפים בין מילים ובקווים אנכיים בין רעיונות. הסר מילות קישור ומילוי. שמור שמות ומספרים בדיוק."
},
"regex": {
"topic_pattern": "[א-ת][א-ת\"'׳״-]{1,}|[A-Za-z][A-Za-z0-9_]{2,}",
"stop_words": "של את על עם אל מן אם כי זה זאת הזו הזה הם הן הוא היא אני אנחנו אתם אתן אתה את היה הייתה היו להיות יש אין גם רק כבר עדיין מאוד יותר פחות כל כמה משהו מישהו שום כאן שם עכשיו אחר כך ואז כן לא טוב תודה בבקשה סליחה נכון לא נכון עבור אחרי לפני בתוך מחוץ דרך לפי בלי כמו עוד פעם מחדש היום אתמול מחר עבודה פרויקט מערכת קובץ קבצים שורה שורות קוד בדיקה בדיקות חיפוש ארמון אגף ארון מגירה",
"quote_pattern": "\"([^\"]{20,200})\"|״([^״]{20,200})״",
"action_pattern": "(?:בנינו|תיקנו|כתבנו|הוספנו|עדכנו|בדקנו|פרסמנו|העברנו|הגדרנו|מחקנו)\\s+[\\w\\sא-ת\"'׳״-]{3,30}"
},
"entity": {
"candidate_pattern": "[א-ת][א-ת\"'׳״-]{1,19}",
"multi_word_pattern": "[א-ת][א-ת\"'׳״-]+(?:\\s+[א-ת][א-ת\"'׳״-]+)+",
"person_verb_patterns": [
"\\b{name}\\s+אמר(?:ה|ו)?\\b",
"\\b{name}\\s+שאל(?:ה|ו)?\\b",
"\\b{name}\\s+סיפר(?:ה|ו)?\\b",
"\\b{name}\\s+ענה(?:ה|ו)?\\b",
"\\b{name}\\s+צחק(?:ה|ו)?\\b",
"\\b{name}\\s+חייכ(?:ה|ו)?\\b",
"\\b{name}\\s+החליט(?:ה|ו)?\\b",
"\\b{name}\\s+כתב(?:ה|ו)?\\b",
"\\b{name}\\s+דחף(?:ה|ו)?\\b",
"\\b{name}\\s+רצה\\b",
"\\b{name}\\s+רצתה\\b",
"\\b{name}\\s+יודע(?:ת|ים)?\\b",
"\\b{name}\\s+חושב(?:ת|ים)?\\b",
"\\bהיי\\s+{name}\\b",
"\\bתודה\\s+{name}\\b",
"\\bשלום\\s+{name}\\b"
],
"pronoun_patterns": [
"\\bהוא\\b",
"\\bהיא\\b",
"\\bהם\\b",
"\\bהן\\b",
"\\bאותו\\b",
"\\bאותה\\b",
"\\bלהם\\b",
"\\bלהן\\b",
"\\bשלו\\b",
"\\bשלה\\b",
"\\bשלהם\\b",
"\\bשלהן\\b"
],
"dialogue_patterns": [
"^>\\s*{name}[:\\s]",
"^{name}:\\s",
"^\\[{name}\\]",
"\"{name}\\s+אמר"
],
"direct_address_pattern": "\\bהיי\\s+{name}\\b|\\bתודה\\s+{name}\\b|\\bשלום\\s+{name}\\b",
"project_verb_patterns": [
"\\bבונים\\s+את\\s+{name}\\b",
"\\bבנינו\\s+את\\s+{name}\\b",
"\\bמשיקים\\s+את\\s+{name}\\b",
"\\bהשקנו\\s+את\\s+{name}\\b",
"\\bמפרסמים\\s+את\\s+{name}\\b",
"\\bפרסמנו\\s+את\\s+{name}\\b",
"\\bמגדירים\\s+את\\s+{name}\\b",
"\\bהגדרנו\\s+את\\s+{name}\\b",
"\\bהארכיטקטורה\\s+של\\s+{name}\\b",
"\\bהמערכת\\s+של\\s+{name}\\b",
"\\b{name}\\.py\\b",
"\\bimport\\s+{name}\\b",
"\\bpip\\s+install\\s+{name}\\b"
],
"stopwords": [
"של",
"את",
"על",
"עם",
"אל",
"מן",
"אם",
"כי",
"זה",
"זאת",
"הזו",
"הזה",
"הוא",
"היא",
"הם",
"הן",
"אני",
"אנחנו",
"אתם",
"אתן",
"אתה",
"את",
"יש",
"אין",
"גם",
"רק",
"כבר",
"עדיין",
"מאוד",
"יותר",
"פחות",
"כל",
"כמה",
"פה",
"כאן",
"שם",
"עכשיו",
"אחר",
"אחרי",
"לפני",
"עוד",
"שוב",
"טוב",
"תודה",
"בבקשה",
"סליחה",
"כן",
"לא",
"נכון",
"משהו",
"מישהו",
"שום",
"פרויקט",
"מערכת",
"קובץ",
"קבצים",
"בדיקה",
"בדיקות",
"שורה",
"שורות",
"קוד",
"חיפוש",
"ארמון",
"אגף",
"ארון",
"מגירה",
"עבודה",
"יום",
"זמן",
"מקום",
"דבר",
"דברים",
"בעיה",
"פתרון",
"גרסה",
"תוצאה",
"אפשרות",
"דוגמה",
"היי",
"שלום",
"בנה",
"בנינו",
"כתב",
"כתבנו",
"הוספנו",
"עדכנו",
"חושב",
"חושבת",
"יודע",
"יודעת"
]
}
}
2 changes: 1 addition & 1 deletion mempalace/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Single source of truth for the MemPalace package version."""

__version__ = "3.3.0"
__version__ = "3.3.1"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mempalace"
version = "3.3.0"
version = "3.3.1"
description = "Give your AI a memory — mine projects and conversations into a searchable palace. No API key required."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
22 changes: 22 additions & 0 deletions tests/test_entity_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,28 @@ def test_extract_candidates_with_cyrillic_locale():
assert "Иван" in result


def test_extract_candidates_with_hebrew_locale():
"""The shipped Hebrew locale catches repeated Hebrew names."""
text = "נועה אמרה שלום. נועה שאלה למה. נועה חייכה. נועה החליטה להצטרף."
result = extract_candidates(text, languages=("en", "he"))
assert "נועה" in result
assert result["נועה"] >= 3


def test_score_entity_hebrew_person_verbs():
"""Hebrew person-verb patterns fire when the Hebrew locale is enabled."""
text = "נועה אמרה שלום. נועה שאלה למה. נועה חייכה. תודה נועה."
lines = text.splitlines()

en_only = score_entity("נועה", text, lines, languages=("en",))
multi = score_entity("נועה", text, lines, languages=("en", "he"))

assert multi["person_score"] > en_only["person_score"]
assert any(
"action" in signal or "addressed" in signal for signal in multi["person_signals"]
)


def test_score_entity_unions_person_verbs_across_languages():
"""A non-English person-verb pattern fires when its locale is enabled."""
locale = {
Expand Down
1 change: 1 addition & 0 deletions tests/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_dialect_compress_samples():
"es": "Decidimos migrar de SQLite a PostgreSQL para mejor escritura concurrente. Ben aprobó el PR ayer.",
"de": "Wir haben beschlossen, von SQLite auf PostgreSQL zu migrieren für bessere gleichzeitige Schreibvorgänge. Ben hat den PR gestern genehmigt.",
"zh-CN": "我们决定从SQLite迁移到PostgreSQL以获得更好的并发写入。Ben昨天批准了PR。",
"he": "החלטנו לעבור מ-SQLite ל-PostgreSQL כדי לשפר כתיבה מקבילית. בן אישר את ה-PR אתמול.",
"id": "Kami memutuskan untuk migrasi dari SQLite ke PostgreSQL untuk penulisan bersamaan yang lebih baik. Ben telah menyetujui PR kemarin.",
}

Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.