@dinasor/mnemo-cli 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -46
- package/README.md +18 -12
- package/VERSION +1 -1
- package/memory.ps1 +24 -5
- package/memory_mac.sh +99 -70
- package/package.json +1 -1
- package/scripts/memory/installer/features/gitignore_setup.ps1 +8 -20
- package/scripts/memory/installer/features/memory_scaffold.ps1 +2 -57
- package/scripts/memory/installer/features/vector_setup.ps1 +29 -0
- package/scripts/memory/installer/templates/mnemo_vector.py +100 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,46 +1,72 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to Mnemo are documented here.
|
|
4
|
-
|
|
5
|
-
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Mnemo uses [Semantic Versioning](https://semver.org/).
|
|
6
|
-
|
|
7
|
-
## [Unreleased]
|
|
8
|
-
|
|
9
|
-
## [0.0.
|
|
10
|
-
|
|
11
|
-
###
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Mnemo are documented here.
|
|
4
|
+
|
|
5
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Mnemo uses [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.0.3] - 2026-02-21
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Vector engine now loads project `.env` values (when `GEMINI_API_KEY` is not already set in the shell) before provider resolution, so local API key setup works more reliably in CLI and MCP contexts.
|
|
13
|
+
- Default vector provider auto-resolves to `gemini` when `MNEMO_PROVIDER` is unset and `GEMINI_API_KEY` is available; otherwise it falls back to `openai`.
|
|
14
|
+
- Vector memory root discovery now prioritizes the script location (repo-local `scripts/memory/mnemo_vector.py`) before current working directory scanning, preventing cross-project context leaks when invoked from another directory.
|
|
15
|
+
- Embedded POSIX fallback vector engine now mirrors the same `.env` and provider-resolution behavior as the primary template.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `mnemo_vector.py` now supports direct CLI operations: `sync`, `search`, `forget`, `health`, and `status`, enabling manual vector workflows outside MCP tool calls.
|
|
19
|
+
|
|
20
|
+
## [0.0.2] - 2026-02-21
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Installers no longer generate root `CLAUDE.md` and `AGENTS.md`; integrations now use canonical `.mnemo/memory/` and agent bridge rules under `.agent/rules/`.
|
|
24
|
+
- Agent rule naming is normalized to ordered files: `00-memory-system.md` and `01-vector-search.md` (vector mode), aligned with cursor rule naming.
|
|
25
|
+
- Installer-managed `.gitignore` now uses top-level Mnemo paths (`.mnemo/`, `.cursor/memory/`, `.cursor/rules/`, `.cursor/mcp.json`, `.agent/rules/`, `scripts/memory/`, `.githooks/`) for cleaner defaults in target repositories.
|
|
26
|
+
- README IDE guidance now points Claude/Codex users to canonical `.mnemo/memory/` retrieval flow and updated agent rule paths.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- PowerShell installer no longer hard-fails when `git config core.hooksPath .githooks` cannot be written (permission/lock); it warns and continues.
|
|
30
|
+
- POSIX vector re-runs are idempotent for autonomy module installation when `--force` is not used.
|
|
31
|
+
- Cross-platform installer regression tests now validate the new numbered agent rule output.
|
|
32
|
+
|
|
33
|
+
## [0.0.1] - 2026-02-21
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- First public Mnemo release with dual installers: `memory.ps1` (Windows PowerShell 5.1+/7+) and `memory_mac.sh` (macOS/Linux POSIX shell).
|
|
37
|
+
- Installer CLI support for `--dry-run` / `-DryRun`, `--force` / `-Force`, project naming, and optional vector enablement.
|
|
38
|
+
- Modular installer architecture with dedicated core and feature modules under `scripts/memory/installer/` (bootstrap, path resolution, I/O, bridges, scaffold, hooks, vector, MCP, `.gitignore`).
|
|
39
|
+
- Canonical memory root at `.mnemo/` with compatibility bridges to `.cursor/` and `.agent/` so existing IDE integrations continue to work.
|
|
40
|
+
- Bridge manager with cross-platform fallback behavior (symlink/junction/hardlink/mirror) and repair/migration logic for legacy layouts.
|
|
41
|
+
- Canonical memory scaffold including always-read files (`hot-rules.md`, `active-context.md`, `memo.md`), lessons, journals, digests, ADR, and templates.
|
|
42
|
+
- Atomic lesson workflow (`L-XXX-*`) and monthly journal workflow with rebuildable indexes/digests.
|
|
43
|
+
- Helper script suite for daily operations (PowerShell + shell variants): rebuild index, lint memory, query memory, add lesson, add journal entry, clear active context.
|
|
44
|
+
- Tag vocabulary enforcement and memory lint guardrails for frontmatter, structure, and token-safety checks.
|
|
45
|
+
- Optional SQLite FTS support (`memory.sqlite`) when Python is available, including build/query helpers.
|
|
46
|
+
- Optional vector mode with `mnemo_vector.py` MCP server and tools: `vector_search`, `vector_sync`, `vector_forget`, `vector_health`, `memory_status`.
|
|
47
|
+
- Vector embedding provider support for both OpenAI and Gemini.
|
|
48
|
+
- Autonomy runtime templates installed with vector mode (`autonomy/*` + `policies.yaml`) for ingestion, lifecycle, reranking, context safety, and policy handling.
|
|
49
|
+
- Portable git hooks via `.githooks/` with automatic `core.hooksPath` setup.
|
|
50
|
+
- Hook automation: `pre-commit` rebuild/lint and optional `post-commit` non-blocking vector sync.
|
|
51
|
+
- Multi-agent bridge outputs under `.cursor/rules/` and `.agent/rules/` for IDE-specific rule loading.
|
|
52
|
+
- Cross-platform CI coverage (Windows/macOS/Ubuntu), regression tests, modularization guardrails, and Python syntax checks for autonomy/retrieval modules.
|
|
53
|
+
- Nightly benchmark workflow for retrieval quality/drift monitoring plus artifact upload.
|
|
54
|
+
- GitHub release workflow for tag-based publishing with preflight/version/changelog validation and packaged installer assets.
|
|
55
|
+
- Governance and contribution assets: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `SUPPORT.md`, issue templates, PR template, `CODEOWNERS`, and Dependabot config.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
- Versioning is centralized in root `VERSION` and consumed by installers and generated output.
|
|
59
|
+
- Installer/runtime path resolution prefers canonical `.mnemo` while preserving `.cursor` compatibility bridges.
|
|
60
|
+
- Python resolution strategy is aligned across memory tooling (`py` / `python3` / `python` fallback behavior where applicable).
|
|
61
|
+
- Managed `.gitignore` and hook behavior now reflect canonical `.mnemo` artifacts while keeping bridge compatibility.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
- Duplicate managed `.gitignore` entries on repeated force installs.
|
|
65
|
+
- Bridge idempotency/self-copy failures around `.cursor/mcp.json` compatibility targets.
|
|
66
|
+
- Version string drift between installer metadata and generated output by using `VERSION` as single source of truth.
|
|
67
|
+
- Python fallback handling in memory query flows that previously depended on a single interpreter name.
|
|
68
|
+
|
|
69
|
+
[Unreleased]: https://github.com/DiNaSoR/Mnemo/compare/v0.0.3...HEAD
|
|
70
|
+
[0.0.3]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.3
|
|
71
|
+
[0.0.2]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.2
|
|
72
|
+
[0.0.1]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.1
|
package/README.md
CHANGED
|
@@ -86,9 +86,9 @@ Use the section that matches your IDE. Each project should run Mnemo install onc
|
|
|
86
86
|
| IDE / Agent | What installer creates | What you do next |
|
|
87
87
|
|---|---|---|
|
|
88
88
|
| Cursor | `.mnemo/rules/cursor/00-memory-system.mdc` (+ `.mnemo/mcp/cursor.mcp.json`, bridged to `.cursor/mcp.json`) | Restart Cursor, run `vector_health`, then `vector_sync` |
|
|
89
|
-
| Claude Code |
|
|
90
|
-
| Gemini Antigravity | `.agent/rules/memory-system.md` | Ensure Antigravity loads project rules from `.agent/rules/` |
|
|
91
|
-
| OpenAI Codex | `
|
|
89
|
+
| Claude Code | `.mnemo/memory/` knowledge base (also visible via `.cursor/memory`) | Open repo in Claude Code and follow retrieval order from `.mnemo/memory/index.md` |
|
|
90
|
+
| Gemini Antigravity | `.agent/rules/00-memory-system.md` (+ `.agent/rules/01-vector-search.md` in vector mode) | Ensure Antigravity loads project rules from `.agent/rules/` |
|
|
91
|
+
| OpenAI Codex | `.mnemo/memory/` knowledge base (also visible via `.cursor/memory`) | Start Codex from repo root and follow retrieval order from `.mnemo/memory/index.md` |
|
|
92
92
|
| Windsurf / Other IDEs | `.mnemo/memory/` knowledge base (also visible at `.cursor/memory/`) | Point memory/context path to `.mnemo/memory/` |
|
|
93
93
|
|
|
94
94
|
### 1) Cursor IDE 🟦
|
|
@@ -105,23 +105,23 @@ You should see MCP tools:
|
|
|
105
105
|
### 2) Claude Code 🤝
|
|
106
106
|
|
|
107
107
|
1. Run Mnemo install in your project root.
|
|
108
|
-
2. Confirm
|
|
108
|
+
2. Confirm `.mnemo/memory/` exists (and `.cursor/memory/` bridge is present).
|
|
109
109
|
3. Start Claude Code from the project root.
|
|
110
|
-
4.
|
|
110
|
+
4. Follow retrieval order from `.mnemo/memory/index.md`.
|
|
111
111
|
|
|
112
112
|
### 3) Gemini Antigravity 🔷
|
|
113
113
|
|
|
114
114
|
1. Run Mnemo install in your project root.
|
|
115
|
-
2. Confirm `.agent/rules/memory-system.md` exists.
|
|
115
|
+
2. Confirm `.agent/rules/00-memory-system.md` exists.
|
|
116
116
|
3. Ensure your Antigravity setup loads `.agent/rules/`.
|
|
117
117
|
4. (Optional) Enable vector mode for semantic memory workflows.
|
|
118
118
|
|
|
119
119
|
### 4) OpenAI Codex 🧪
|
|
120
120
|
|
|
121
121
|
1. Run Mnemo install in your project root.
|
|
122
|
-
2. Confirm
|
|
123
|
-
3. Launch Codex from the same root
|
|
124
|
-
4. Follow
|
|
122
|
+
2. Confirm `.mnemo/memory/` exists (and `.cursor/memory/` bridge is present).
|
|
123
|
+
3. Launch Codex from the same project root.
|
|
124
|
+
4. Follow retrieval order from `.mnemo/memory/index.md`.
|
|
125
125
|
|
|
126
126
|
### 5) Windsurf / Other IDEs 🌊
|
|
127
127
|
|
|
@@ -137,7 +137,7 @@ You should see MCP tools:
|
|
|
137
137
|
- **Always-read layer**: `.mnemo/memory/hot-rules.md`, `active-context.md`, `memo.md`
|
|
138
138
|
- **Atomic lessons**: `.mnemo/memory/lessons/L-XXX-*.md` + generated lesson index
|
|
139
139
|
- **Monthly journal + digests**: `.mnemo/memory/journal/YYYY-MM.md` + `.mnemo/memory/digests/*.digest.md`
|
|
140
|
-
- **Rule enforcement**: `.mnemo/rules/cursor/00-memory-system.mdc` (bridged to `.cursor/rules/`)
|
|
140
|
+
- **Rule enforcement**: `.mnemo/rules/cursor/00-memory-system.mdc` and `.mnemo/rules/agent/00-memory-system.md` (bridged to `.cursor/rules/` and `.agent/rules/`)
|
|
141
141
|
- **Helper scripts**: `scripts/memory/*` (rebuild, lint, query, add-lesson, add-journal-entry, clear-active)
|
|
142
142
|
- **Optional SQLite FTS**: `.mnemo/memory/memory.sqlite` when Python is available
|
|
143
143
|
- **Optional vector layer**: `scripts/memory/mnemo_vector.py` + MCP tools
|
|
@@ -183,7 +183,8 @@ Runner triggers:
|
|
|
183
183
|
00-memory-system.mdc
|
|
184
184
|
01-vector-search.mdc # vector mode only
|
|
185
185
|
agent/
|
|
186
|
-
memory-system.md
|
|
186
|
+
00-memory-system.md
|
|
187
|
+
01-vector-search.md # vector mode only
|
|
187
188
|
mcp/
|
|
188
189
|
cursor.mcp.json # vector mode only
|
|
189
190
|
|
|
@@ -217,7 +218,7 @@ scripts/
|
|
|
217
218
|
| `add-lesson.ps1` | Creates next `L-XXX` lesson with normalized tags |
|
|
218
219
|
| `add-journal-entry.ps1` | Adds entry under current date in monthly journal |
|
|
219
220
|
| `clear-active.ps1` | Resets `active-context.md` |
|
|
220
|
-
| `mnemo_vector.py` | Vector sync
|
|
221
|
+
| `mnemo_vector.py` | Vector MCP server + CLI (`sync`, `search`, `forget`, `health`, `status`) in vector mode |
|
|
221
222
|
|
|
222
223
|
## 🔐 Git hooks and API keys
|
|
223
224
|
|
|
@@ -229,12 +230,17 @@ Mnemo auto-configures `core.hooksPath` to `.githooks` and installs:
|
|
|
229
230
|
Important:
|
|
230
231
|
- Cursor MCP tools read API keys from `.mnemo/mcp/cursor.mcp.json` env placeholders (`.cursor/mcp.json` stays bridged).
|
|
231
232
|
- Git hooks read API keys from your shell environment.
|
|
233
|
+
- If `GEMINI_API_KEY` is not already in the environment, `scripts/memory/mnemo_vector.py` also tries loading keys from project-root `.env`.
|
|
232
234
|
|
|
233
235
|
```sh
|
|
234
236
|
# bash/zsh example
|
|
235
237
|
export OPENAI_API_KEY="sk-..."
|
|
236
238
|
# or
|
|
237
239
|
export GEMINI_API_KEY="..."
|
|
240
|
+
|
|
241
|
+
# optional direct CLI usage (outside MCP tool calls)
|
|
242
|
+
python3 scripts/memory/mnemo_vector.py sync
|
|
243
|
+
python3 scripts/memory/mnemo_vector.py health
|
|
238
244
|
```
|
|
239
245
|
|
|
240
246
|
## ✅ Recommended daily workflow
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.3
|
package/memory.ps1
CHANGED
|
@@ -108,10 +108,29 @@ Ensure-MnemoCanonicalBridges -Ctx $ctx
|
|
|
108
108
|
|
|
109
109
|
# Auto-configure portable hooks path (removes the manual 'git config' step)
|
|
110
110
|
if (-not $DryRun -and (Test-Path $ctx.GitDir)) {
|
|
111
|
-
$
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
$nativeErrorPrefVar = Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue
|
|
112
|
+
$nativeErrorPrefOld = $null
|
|
113
|
+
if ($null -ne $nativeErrorPrefVar) {
|
|
114
|
+
$nativeErrorPrefOld = $PSNativeCommandUseErrorActionPreference
|
|
115
|
+
$PSNativeCommandUseErrorActionPreference = $false
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
$currentHooksPath = (& git -C $RepoRoot config core.hooksPath 2>$null | Out-String).Trim()
|
|
120
|
+
if ($currentHooksPath -ne ".githooks") {
|
|
121
|
+
$null = & git -C $RepoRoot config core.hooksPath .githooks 2>$null
|
|
122
|
+
if ($LASTEXITCODE -eq 0) {
|
|
123
|
+
Write-Host "Configured: git config core.hooksPath .githooks" -ForegroundColor Green
|
|
124
|
+
} else {
|
|
125
|
+
Write-Warning "Could not set git core.hooksPath automatically (permission denied or repo locked). Continuing."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
Write-Warning "Could not set git core.hooksPath automatically. Continuing."
|
|
130
|
+
} finally {
|
|
131
|
+
if ($null -ne $nativeErrorPrefVar) {
|
|
132
|
+
$PSNativeCommandUseErrorActionPreference = $nativeErrorPrefOld
|
|
133
|
+
}
|
|
115
134
|
}
|
|
116
135
|
}
|
|
117
136
|
|
|
@@ -140,7 +159,7 @@ Write-Host ""
|
|
|
140
159
|
if ($EnableVector -and (-not $DryRun)) {
|
|
141
160
|
Write-Host "Vector tools enabled ($VectorProvider):" -ForegroundColor Cyan
|
|
142
161
|
Write-Host " MCP tools: vector_search, vector_sync, vector_forget, vector_health, memory_status" -ForegroundColor DarkGray
|
|
143
|
-
Write-Host "
|
|
162
|
+
Write-Host " Rules: .cursor/rules/01-vector-search.mdc and .agent/rules/01-vector-search.md" -ForegroundColor DarkGray
|
|
144
163
|
Write-Host " MCP: .cursor/mcp.json -> MnemoVector server" -ForegroundColor DarkGray
|
|
145
164
|
Write-Host ""
|
|
146
165
|
Write-Host "Autonomous memory runtime:" -ForegroundColor Cyan
|
package/memory_mac.sh
CHANGED
|
@@ -561,36 +561,12 @@ SEARCH FIRST, THEN FETCH:
|
|
|
561
561
|
EOF
|
|
562
562
|
|
|
563
563
|
# -------------------------
|
|
564
|
-
#
|
|
564
|
+
# Agent bridge files
|
|
565
565
|
# -------------------------
|
|
566
566
|
|
|
567
567
|
mkdir -p "$AGENT_RULES_DIR"
|
|
568
568
|
|
|
569
|
-
write_file "$
|
|
570
|
-
# Project Memory (Mnemo)
|
|
571
|
-
|
|
572
|
-
This project uses Mnemo for structured AI memory.
|
|
573
|
-
Memory lives in `.cursor/memory/` as the single source of truth.
|
|
574
|
-
|
|
575
|
-
## Read Order (ALWAYS)
|
|
576
|
-
1. `.cursor/memory/hot-rules.md` - tiny invariants (<20 lines)
|
|
577
|
-
2. `.cursor/memory/active-context.md` - current session state
|
|
578
|
-
3. `.cursor/memory/memo.md` - long-term project truth + ownership
|
|
579
|
-
|
|
580
|
-
## Search First, Then Fetch
|
|
581
|
-
- `.cursor/memory/lessons/index.md` → find lesson ID → open only that lesson file
|
|
582
|
-
- `.cursor/memory/digests/YYYY-MM.digest.md` → before raw journal archaeology
|
|
583
|
-
- `.cursor/memory/journal/YYYY-MM.md` → only for deep history
|
|
584
|
-
|
|
585
|
-
## After Any Feature/Fix
|
|
586
|
-
1. Update `active-context.md` during work
|
|
587
|
-
2. Add journal entry when done
|
|
588
|
-
3. Create lesson if you discovered a pitfall
|
|
589
|
-
4. Update `memo.md` if project truth changed
|
|
590
|
-
5. Clear `active-context.md` when task is merged
|
|
591
|
-
EOF
|
|
592
|
-
|
|
593
|
-
write_file "$AGENT_RULES_DIR/memory-system.md" <<'EOF'
|
|
569
|
+
write_file "$AGENT_RULES_DIR/00-memory-system.md" <<'EOF'
|
|
594
570
|
---
|
|
595
571
|
description: Mnemo memory system - structured AI memory in .cursor/memory/
|
|
596
572
|
alwaysApply: true
|
|
@@ -623,28 +599,6 @@ This project uses Mnemo for structured AI memory. All memory lives in `.cursor/m
|
|
|
623
599
|
- Clear active-context.md when task is merged
|
|
624
600
|
EOF
|
|
625
601
|
|
|
626
|
-
write_file "$REPO_ROOT/AGENTS.md" <<'EOF'
|
|
627
|
-
# Memory System (Mnemo)
|
|
628
|
-
|
|
629
|
-
This project uses Mnemo for structured AI memory.
|
|
630
|
-
Memory location: `.cursor/memory/`
|
|
631
|
-
|
|
632
|
-
## Retrieval Order
|
|
633
|
-
1. Read `.cursor/memory/hot-rules.md` first (tiny, <20 lines)
|
|
634
|
-
2. Read `.cursor/memory/active-context.md` for current session
|
|
635
|
-
3. Read `.cursor/memory/memo.md` for project truth + ownership
|
|
636
|
-
4. Search `.cursor/memory/lessons/index.md` before creating new patterns
|
|
637
|
-
5. Check `.cursor/memory/digests/` before raw journal archaeology
|
|
638
|
-
|
|
639
|
-
## Authority Order (highest to lowest)
|
|
640
|
-
1. Lessons override EVERYTHING
|
|
641
|
-
2. active-context.md overrides memo/journal (but NOT lessons)
|
|
642
|
-
3. memo.md is long-term project truth
|
|
643
|
-
4. Journal is history
|
|
644
|
-
5. Existing codebase
|
|
645
|
-
6. New suggestions (lowest priority)
|
|
646
|
-
EOF
|
|
647
|
-
|
|
648
602
|
write_file "$MEM_SCRIPTS_DIR/customization.md" <<'EOF'
|
|
649
603
|
# Mnemo Memory Customization Prompt (paste into an AI)
|
|
650
604
|
|
|
@@ -1867,9 +1821,70 @@ from mcp.server.fastmcp import FastMCP
|
|
|
1867
1821
|
|
|
1868
1822
|
SCHEMA_VERSION = 2
|
|
1869
1823
|
EMBED_DIM = 1536
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def _resolve_memory_root() -> Path:
|
|
1827
|
+
override = os.getenv("MNEMO_MEMORY_ROOT", "").strip()
|
|
1828
|
+
if override:
|
|
1829
|
+
return Path(override).expanduser().resolve()
|
|
1830
|
+
|
|
1831
|
+
script_repo = Path(__file__).resolve().parents[2]
|
|
1832
|
+
for rel in ((".mnemo", "memory"), (".cursor", "memory")):
|
|
1833
|
+
candidate = script_repo.joinpath(*rel)
|
|
1834
|
+
if candidate.exists():
|
|
1835
|
+
return candidate
|
|
1836
|
+
|
|
1837
|
+
cwd = Path.cwd().resolve()
|
|
1838
|
+
for root in (cwd, *cwd.parents):
|
|
1839
|
+
for rel in ((".mnemo", "memory"), (".cursor", "memory")):
|
|
1840
|
+
candidate = root.joinpath(*rel)
|
|
1841
|
+
if candidate.exists():
|
|
1842
|
+
return candidate
|
|
1843
|
+
return script_repo / ".mnemo" / "memory"
|
|
1844
|
+
|
|
1845
|
+
|
|
1846
|
+
def _parse_env_line(raw_line: str):
|
|
1847
|
+
line = raw_line.strip()
|
|
1848
|
+
if not line or line.startswith("#"):
|
|
1849
|
+
return None
|
|
1850
|
+
if line.startswith("export "):
|
|
1851
|
+
line = line[7:].strip()
|
|
1852
|
+
if "=" not in line:
|
|
1853
|
+
return None
|
|
1854
|
+
key, value = line.split("=", 1)
|
|
1855
|
+
key = key.strip()
|
|
1856
|
+
if not key or any(ch.isspace() for ch in key):
|
|
1857
|
+
return None
|
|
1858
|
+
value = value.strip()
|
|
1859
|
+
if value and len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
|
1860
|
+
value = value[1:-1]
|
|
1861
|
+
elif " #" in value:
|
|
1862
|
+
value = value.split(" #", 1)[0].rstrip()
|
|
1863
|
+
return key, value
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
def _load_project_env() -> None:
|
|
1867
|
+
if os.getenv("GEMINI_API_KEY"):
|
|
1868
|
+
return
|
|
1869
|
+
env_path = Path(".env")
|
|
1870
|
+
if not env_path.exists():
|
|
1871
|
+
return
|
|
1872
|
+
try:
|
|
1873
|
+
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
|
1874
|
+
parsed = _parse_env_line(raw_line)
|
|
1875
|
+
if not parsed:
|
|
1876
|
+
continue
|
|
1877
|
+
key, value = parsed
|
|
1878
|
+
os.environ.setdefault(key, value)
|
|
1879
|
+
except OSError:
|
|
1880
|
+
pass
|
|
1881
|
+
|
|
1882
|
+
|
|
1883
|
+
MEM_ROOT = _resolve_memory_root()
|
|
1884
|
+
_DB_OVERRIDE = os.getenv("MNEMO_DB_PATH", "").strip()
|
|
1885
|
+
DB_PATH = Path(_DB_OVERRIDE).expanduser().resolve() if _DB_OVERRIDE else (MEM_ROOT / "mnemo_vector.sqlite")
|
|
1886
|
+
_load_project_env()
|
|
1887
|
+
PROVIDER = os.getenv("MNEMO_PROVIDER", "gemini" if os.getenv("GEMINI_API_KEY") else "openai").lower()
|
|
1873
1888
|
|
|
1874
1889
|
SKIP_NAMES = {
|
|
1875
1890
|
"README.md",
|
|
@@ -2202,6 +2217,32 @@ This rule supplements `00-memory-system.mdc` and does not replace governance.
|
|
|
2202
2217
|
- `vector_forget` - remove stale entries.
|
|
2203
2218
|
- `vector_health` - DB/API health check.
|
|
2204
2219
|
|
|
2220
|
+
## Fallback
|
|
2221
|
+
If vector search is unavailable, keep using:
|
|
2222
|
+
- `scripts/memory/query-memory.sh --query "..."`
|
|
2223
|
+
- `scripts/memory/query-memory.sh --query "..." --use-sqlite`
|
|
2224
|
+
EOF
|
|
2225
|
+
|
|
2226
|
+
write_file "$AGENT_RULES_DIR/01-vector-search.md" <<'EOF'
|
|
2227
|
+
---
|
|
2228
|
+
description: Mnemo vector semantic retrieval layer (optional)
|
|
2229
|
+
alwaysApply: true
|
|
2230
|
+
---
|
|
2231
|
+
|
|
2232
|
+
# Vector Memory Layer (Optional)
|
|
2233
|
+
|
|
2234
|
+
This rule supplements `00-memory-system.md` and does not replace governance.
|
|
2235
|
+
|
|
2236
|
+
## Use vector tools when:
|
|
2237
|
+
- You do not know the exact keyword for prior context.
|
|
2238
|
+
- Keyword/FTS search did not find relevant history.
|
|
2239
|
+
|
|
2240
|
+
## MCP tools
|
|
2241
|
+
- `vector_search` - semantic retrieval with cosine similarity.
|
|
2242
|
+
- `vector_sync` - incremental indexing.
|
|
2243
|
+
- `vector_forget` - remove stale entries.
|
|
2244
|
+
- `vector_health` - DB/API health check.
|
|
2245
|
+
|
|
2205
2246
|
## Fallback
|
|
2206
2247
|
If vector search is unavailable, keep using:
|
|
2207
2248
|
- `scripts/memory/query-memory.sh --query "..."`
|
|
@@ -2344,25 +2385,13 @@ gi="$REPO_ROOT/.gitignore"
|
|
|
2344
2385
|
GI_BEGIN="# >>> Mnemo (generated) - do not edit this block manually <<<"
|
|
2345
2386
|
GI_END="# <<< Mnemo (generated) >>>"
|
|
2346
2387
|
|
|
2347
|
-
ignore_lines=".mnemo/
|
|
2348
|
-
.cursor/memory/
|
|
2349
|
-
.
|
|
2350
|
-
.cursor/mcp.json
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
.
|
|
2354
|
-
.mnemo/memory/mnemo_vector.sqlite-journal
|
|
2355
|
-
.mnemo/memory/mnemo_vector.sqlite-wal
|
|
2356
|
-
.mnemo/memory/mnemo_vector.sqlite-shm
|
|
2357
|
-
.mnemo/memory/.sync.lock
|
|
2358
|
-
.mnemo/memory/.autonomy/
|
|
2359
|
-
.cursor/memory/mnemo_vector.sqlite
|
|
2360
|
-
.cursor/memory/mnemo_vector.sqlite-journal
|
|
2361
|
-
.cursor/memory/mnemo_vector.sqlite-wal
|
|
2362
|
-
.cursor/memory/mnemo_vector.sqlite-shm
|
|
2363
|
-
.cursor/memory/.sync.lock
|
|
2364
|
-
.cursor/memory/.autonomy/"
|
|
2365
|
-
fi
|
|
2388
|
+
ignore_lines=".mnemo/
|
|
2389
|
+
.cursor/memory/
|
|
2390
|
+
.cursor/rules/
|
|
2391
|
+
.cursor/mcp.json
|
|
2392
|
+
.agent/rules/
|
|
2393
|
+
scripts/memory/
|
|
2394
|
+
.githooks/"
|
|
2366
2395
|
|
|
2367
2396
|
if [ "$DRY_RUN" = "1" ]; then
|
|
2368
2397
|
echo "[DRY RUN] WOULD UPDATE: $gi (managed Mnemo block)"
|
package/package.json
CHANGED
|
@@ -13,28 +13,16 @@ function Update-MnemoGitignore {
|
|
|
13
13
|
$giBeginMarker = "# >>> Mnemo (generated) - do not edit this block manually <<<"
|
|
14
14
|
$giEndMarker = "# <<< Mnemo (generated) >>>"
|
|
15
15
|
|
|
16
|
+
# User-facing default: ignore the full Mnemo generated footprint so target repos stay clean by default.
|
|
16
17
|
$ignoreLines = @(
|
|
17
|
-
".mnemo/
|
|
18
|
-
".cursor/memory/
|
|
19
|
-
".
|
|
20
|
-
".cursor/mcp.json"
|
|
18
|
+
".mnemo/",
|
|
19
|
+
".cursor/memory/",
|
|
20
|
+
".cursor/rules/",
|
|
21
|
+
".cursor/mcp.json",
|
|
22
|
+
".agent/rules/",
|
|
23
|
+
"scripts/memory/",
|
|
24
|
+
".githooks/"
|
|
21
25
|
)
|
|
22
|
-
if ($EnableVector) {
|
|
23
|
-
$ignoreLines += @(
|
|
24
|
-
".mnemo/memory/mnemo_vector.sqlite",
|
|
25
|
-
".mnemo/memory/mnemo_vector.sqlite-journal",
|
|
26
|
-
".mnemo/memory/mnemo_vector.sqlite-wal",
|
|
27
|
-
".mnemo/memory/mnemo_vector.sqlite-shm",
|
|
28
|
-
".mnemo/memory/.sync.lock",
|
|
29
|
-
".mnemo/memory/.autonomy/",
|
|
30
|
-
".cursor/memory/mnemo_vector.sqlite",
|
|
31
|
-
".cursor/memory/mnemo_vector.sqlite-journal",
|
|
32
|
-
".cursor/memory/mnemo_vector.sqlite-wal",
|
|
33
|
-
".cursor/memory/mnemo_vector.sqlite-shm",
|
|
34
|
-
".cursor/memory/.sync.lock",
|
|
35
|
-
".cursor/memory/.autonomy/"
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
26
|
|
|
39
27
|
$giLineEndings = "CRLF"
|
|
40
28
|
$giContent = ""
|
|
@@ -189,30 +189,6 @@ SEARCH FIRST, THEN FETCH:
|
|
|
189
189
|
- When you discover a bug pattern -> suggest creating a lesson
|
|
190
190
|
- When unsure about architecture -> check lessons/index.md first
|
|
191
191
|
- Don't create parallel systems -> check memo.md ownership map
|
|
192
|
-
"@
|
|
193
|
-
|
|
194
|
-
$claudeMd = @"
|
|
195
|
-
# Project Memory (Mnemo)
|
|
196
|
-
|
|
197
|
-
This project uses [Mnemo](https://github.com/DiNaSoR/Mnemo) for structured AI memory.
|
|
198
|
-
Memory lives in ``.cursor/memory/`` as the single source of truth.
|
|
199
|
-
|
|
200
|
-
## Read Order (ALWAYS)
|
|
201
|
-
1. ``.cursor/memory/hot-rules.md`` - tiny invariants (<20 lines)
|
|
202
|
-
2. ``.cursor/memory/active-context.md`` - current session state
|
|
203
|
-
3. ``.cursor/memory/memo.md`` - long-term project truth + ownership
|
|
204
|
-
|
|
205
|
-
## Search First, Then Fetch
|
|
206
|
-
- ``.cursor/memory/lessons/index.md`` -> find lesson ID -> open only that lesson file
|
|
207
|
-
- ``.cursor/memory/digests/YYYY-MM.digest.md`` -> before raw journal archaeology
|
|
208
|
-
- ``.cursor/memory/journal/YYYY-MM.md`` -> only for deep history
|
|
209
|
-
|
|
210
|
-
## After Any Feature/Fix
|
|
211
|
-
1. Update ``active-context.md`` during work
|
|
212
|
-
2. Add journal entry when done
|
|
213
|
-
3. Create lesson if you discovered a pitfall
|
|
214
|
-
4. Update ``memo.md`` if project truth changed
|
|
215
|
-
5. Clear ``active-context.md`` when task is merged
|
|
216
192
|
"@
|
|
217
193
|
|
|
218
194
|
$static = @{
|
|
@@ -222,7 +198,6 @@ Memory lives in ``.cursor/memory/`` as the single source of truth.
|
|
|
222
198
|
(Join-Path $Ctx.MemoryDir "memo.md") = $memo
|
|
223
199
|
(Join-Path $Ctx.JournalDir "$month.md") = $journalMonth
|
|
224
200
|
(Join-Path $Ctx.RulesDir "00-memory-system.mdc") = $memoryRule
|
|
225
|
-
(Join-Path $Ctx.RepoRoot "CLAUDE.md") = $claudeMd
|
|
226
201
|
}
|
|
227
202
|
|
|
228
203
|
foreach ($kv in $static.GetEnumerator()) {
|
|
@@ -397,7 +372,7 @@ What did we choose?
|
|
|
397
372
|
Tradeoffs, risks, follow-ups.
|
|
398
373
|
"@ -ForceWrite:$Force
|
|
399
374
|
|
|
400
|
-
#
|
|
375
|
+
# Agent bridge rules
|
|
401
376
|
$geminiRule = @"
|
|
402
377
|
---
|
|
403
378
|
description: Mnemo memory system - structured AI memory in .cursor/memory/
|
|
@@ -426,37 +401,7 @@ This project uses Mnemo for structured AI memory. All memory lives in ``.cursor/
|
|
|
426
401
|
- Clear active-context.md when task is merged
|
|
427
402
|
"@
|
|
428
403
|
|
|
429
|
-
$
|
|
430
|
-
# Memory System (Mnemo)
|
|
431
|
-
|
|
432
|
-
This project uses Mnemo for structured AI memory.
|
|
433
|
-
Memory location: ``.cursor/memory/``
|
|
434
|
-
|
|
435
|
-
## Retrieval Order
|
|
436
|
-
1. Read ``.cursor/memory/hot-rules.md`` first (tiny, <20 lines)
|
|
437
|
-
2. Read ``.cursor/memory/active-context.md`` for current session
|
|
438
|
-
3. Read ``.cursor/memory/memo.md`` for project truth + ownership
|
|
439
|
-
4. Search ``.cursor/memory/lessons/index.md`` before creating new patterns
|
|
440
|
-
5. Check ``.cursor/memory/digests/`` before raw journal archaeology
|
|
441
|
-
|
|
442
|
-
## Authority Order (highest to lowest)
|
|
443
|
-
1. Lessons override EVERYTHING
|
|
444
|
-
2. active-context.md overrides memo/journal (but NOT lessons)
|
|
445
|
-
3. memo.md is long-term project truth
|
|
446
|
-
4. Journal is history
|
|
447
|
-
5. Existing codebase
|
|
448
|
-
6. New suggestions (lowest priority)
|
|
449
|
-
|
|
450
|
-
## After Any Feature/Fix
|
|
451
|
-
- Update active-context.md during work (scratchpad)
|
|
452
|
-
- Add journal entry to journal/YYYY-MM.md when done
|
|
453
|
-
- Create lessons/L-XXX-title.md if you discovered a pitfall
|
|
454
|
-
- Update memo.md if project truth changed
|
|
455
|
-
- Clear active-context.md when task is merged
|
|
456
|
-
"@
|
|
457
|
-
|
|
458
|
-
Write-MnemoFile (Join-Path $Ctx.MnemoRulesAgentDir "memory-system.md") $geminiRule -ForceWrite:$Force
|
|
459
|
-
Write-MnemoFile (Join-Path $Ctx.RepoRoot "AGENTS.md") $agentsMd -ForceWrite:$Force
|
|
404
|
+
Write-MnemoFile (Join-Path $Ctx.MnemoRulesAgentDir "00-memory-system.md") $geminiRule -ForceWrite:$Force
|
|
460
405
|
|
|
461
406
|
Write-Host "`nMemory scaffold installed." -ForegroundColor Cyan
|
|
462
407
|
}
|
|
@@ -99,5 +99,34 @@ If vector search is unavailable, keep using:
|
|
|
99
99
|
|
|
100
100
|
Write-MnemoFile (Join-Path $Ctx.RulesDir "01-vector-search.mdc") $vectorRule -ForceWrite:$Force
|
|
101
101
|
|
|
102
|
+
$agentVectorRule = @"
|
|
103
|
+
---
|
|
104
|
+
description: Mnemo vector semantic retrieval layer (optional)
|
|
105
|
+
alwaysApply: true
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# Vector Memory Layer (Optional)
|
|
109
|
+
|
|
110
|
+
This rule supplements ``00-memory-system.md`` and does not replace governance.
|
|
111
|
+
|
|
112
|
+
## Use vector tools when:
|
|
113
|
+
- You do not know the exact keyword for prior context.
|
|
114
|
+
- Keyword/FTS search did not find relevant history.
|
|
115
|
+
|
|
116
|
+
## MCP tools
|
|
117
|
+
- ``vector_search`` - semantic retrieval with authority-aware reranking.
|
|
118
|
+
- ``vector_sync`` - incremental indexing.
|
|
119
|
+
- ``vector_forget`` - remove stale entries.
|
|
120
|
+
- ``vector_health`` - DB/API health check.
|
|
121
|
+
- ``memory_status`` - JSON summary for autonomous monitoring.
|
|
122
|
+
|
|
123
|
+
## Fallback
|
|
124
|
+
If vector search is unavailable, keep using:
|
|
125
|
+
- ``scripts/memory/query-memory.ps1 -Query "..."``
|
|
126
|
+
- ``scripts/memory/query-memory.ps1 -Query "..." -UseSqlite``
|
|
127
|
+
"@
|
|
128
|
+
|
|
129
|
+
Write-MnemoFile (Join-Path $Ctx.MnemoRulesAgentDir "01-vector-search.md") $agentVectorRule -ForceWrite:$Force
|
|
130
|
+
|
|
102
131
|
return $vectorPython
|
|
103
132
|
}
|
|
@@ -9,6 +9,8 @@ import re
|
|
|
9
9
|
import json
|
|
10
10
|
import sqlite3
|
|
11
11
|
import hashlib
|
|
12
|
+
import argparse
|
|
13
|
+
import sys
|
|
12
14
|
from pathlib import Path
|
|
13
15
|
|
|
14
16
|
import sqlite_vec
|
|
@@ -27,19 +29,78 @@ def _resolve_memory_root() -> Path:
|
|
|
27
29
|
if override:
|
|
28
30
|
return Path(override).expanduser().resolve()
|
|
29
31
|
|
|
32
|
+
script_repo = Path(__file__).resolve().parents[2]
|
|
33
|
+
for rel in ((".mnemo", "memory"), (".cursor", "memory")):
|
|
34
|
+
candidate = script_repo.joinpath(*rel)
|
|
35
|
+
if candidate.exists():
|
|
36
|
+
return candidate
|
|
37
|
+
|
|
30
38
|
cwd = Path.cwd().resolve()
|
|
31
39
|
for root in (cwd, *cwd.parents):
|
|
32
40
|
for rel in ((".mnemo", "memory"), (".cursor", "memory")):
|
|
33
41
|
candidate = root.joinpath(*rel)
|
|
34
42
|
if candidate.exists():
|
|
35
43
|
return candidate
|
|
36
|
-
return
|
|
44
|
+
return script_repo / ".mnemo" / "memory"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _resolve_repo_root(memory_root: Path) -> Path:
|
|
48
|
+
root = memory_root.resolve()
|
|
49
|
+
if root.name == "memory" and root.parent.name in {".mnemo", ".cursor"}:
|
|
50
|
+
return root.parent.parent
|
|
51
|
+
cwd = Path.cwd().resolve()
|
|
52
|
+
for candidate in (cwd, *cwd.parents):
|
|
53
|
+
if candidate.joinpath(".mnemo", "memory").exists() or candidate.joinpath(".cursor", "memory").exists():
|
|
54
|
+
return candidate
|
|
55
|
+
return cwd
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _parse_env_line(raw_line: str) -> tuple[str, str] | None:
|
|
59
|
+
line = raw_line.strip()
|
|
60
|
+
if not line or line.startswith("#"):
|
|
61
|
+
return None
|
|
62
|
+
if line.startswith("export "):
|
|
63
|
+
line = line[7:].strip()
|
|
64
|
+
if "=" not in line:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
key, value = line.split("=", 1)
|
|
68
|
+
key = key.strip()
|
|
69
|
+
if not key or any(ch.isspace() for ch in key):
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
value = value.strip()
|
|
73
|
+
if value and len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
|
74
|
+
value = value[1:-1]
|
|
75
|
+
elif " #" in value:
|
|
76
|
+
value = value.split(" #", 1)[0].rstrip()
|
|
77
|
+
return key, value
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _load_project_env(repo_root: Path) -> None:
|
|
81
|
+
# Keep shell-provided values authoritative; only fill missing vars from .env.
|
|
82
|
+
if os.getenv("GEMINI_API_KEY"):
|
|
83
|
+
return
|
|
84
|
+
env_path = repo_root / ".env"
|
|
85
|
+
if not env_path.exists():
|
|
86
|
+
return
|
|
87
|
+
try:
|
|
88
|
+
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
|
89
|
+
parsed = _parse_env_line(raw_line)
|
|
90
|
+
if not parsed:
|
|
91
|
+
continue
|
|
92
|
+
key, value = parsed
|
|
93
|
+
os.environ.setdefault(key, value)
|
|
94
|
+
except OSError:
|
|
95
|
+
pass
|
|
37
96
|
|
|
38
97
|
|
|
39
98
|
MEM_ROOT = _resolve_memory_root()
|
|
99
|
+
REPO_ROOT = _resolve_repo_root(MEM_ROOT)
|
|
100
|
+
_load_project_env(REPO_ROOT)
|
|
40
101
|
_DB_OVERRIDE = os.getenv("MNEMO_DB_PATH", "").strip()
|
|
41
102
|
DB_PATH = Path(_DB_OVERRIDE).expanduser().resolve() if _DB_OVERRIDE else (MEM_ROOT / "mnemo_vector.sqlite")
|
|
42
|
-
PROVIDER = os.getenv("MNEMO_PROVIDER", "openai").lower()
|
|
103
|
+
PROVIDER = os.getenv("MNEMO_PROVIDER", "gemini" if os.getenv("GEMINI_API_KEY") else "openai").lower()
|
|
43
104
|
|
|
44
105
|
SKIP_NAMES = {
|
|
45
106
|
"README.md", "index.md", "lessons-index.json",
|
|
@@ -552,5 +613,42 @@ def memory_status() -> str:
|
|
|
552
613
|
return json.dumps({"error": str(e)})
|
|
553
614
|
|
|
554
615
|
|
|
616
|
+
def _run_cli(argv: list[str]) -> int:
|
|
617
|
+
parser = argparse.ArgumentParser(description="Mnemo vector CLI")
|
|
618
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
619
|
+
|
|
620
|
+
sub.add_parser("sync", help="Rebuild vector index from memory markdown files")
|
|
621
|
+
|
|
622
|
+
p_search = sub.add_parser("search", help="Semantic search memory")
|
|
623
|
+
p_search.add_argument("query", help="Search query text")
|
|
624
|
+
p_search.add_argument("--top-k", type=int, default=8, help="Number of results to return")
|
|
625
|
+
|
|
626
|
+
p_forget = sub.add_parser("forget", help="Remove vectors by source path")
|
|
627
|
+
p_forget.add_argument("ref_path", help="Reference path to remove (exact match)")
|
|
628
|
+
|
|
629
|
+
sub.add_parser("health", help="Check DB and embedding provider health")
|
|
630
|
+
sub.add_parser("status", help="Return JSON memory status summary")
|
|
631
|
+
|
|
632
|
+
args = parser.parse_args(argv)
|
|
633
|
+
try:
|
|
634
|
+
if args.command == "sync":
|
|
635
|
+
print(vector_sync())
|
|
636
|
+
elif args.command == "search":
|
|
637
|
+
print(vector_search(args.query, top_k=args.top_k))
|
|
638
|
+
elif args.command == "forget":
|
|
639
|
+
print(vector_forget(args.ref_path))
|
|
640
|
+
elif args.command == "health":
|
|
641
|
+
print(vector_health())
|
|
642
|
+
elif args.command == "status":
|
|
643
|
+
print(memory_status())
|
|
644
|
+
except Exception as e:
|
|
645
|
+
print(f"ERROR: {e}", file=sys.stderr)
|
|
646
|
+
return 1
|
|
647
|
+
return 0
|
|
648
|
+
|
|
649
|
+
|
|
555
650
|
if __name__ == "__main__":
|
|
651
|
+
cli_commands = {"sync", "search", "forget", "health", "status"}
|
|
652
|
+
if len(sys.argv) > 1 and sys.argv[1] in cli_commands:
|
|
653
|
+
raise SystemExit(_run_cli(sys.argv[1:]))
|
|
556
654
|
mcp.run()
|