@dinasor/mnemo-cli 0.0.2 → 0.0.4

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 CHANGED
@@ -6,6 +6,26 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Mnemo u
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.0.4] - 2026-02-22
10
+
11
+ ### Added
12
+ - Installers now seed a project-level Cursor skill at `.cursor/skills/mnemo-codebase-optimizer/` (`SKILL.md` + `reference.md`) to accelerate high-signal memory bootstrapping for any codebase.
13
+
14
+ ### Changed
15
+ - Installer-managed `.gitignore` now includes `.cursor/skills/` to keep generated Mnemo skill artifacts out of default project tracking.
16
+ - Windows and POSIX regression tests now assert skill generation so installer behavior stays consistent across platforms.
17
+
18
+ ## [0.0.3] - 2026-02-21
19
+
20
+ ### Changed
21
+ - 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.
22
+ - Default vector provider auto-resolves to `gemini` when `MNEMO_PROVIDER` is unset and `GEMINI_API_KEY` is available; otherwise it falls back to `openai`.
23
+ - 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.
24
+ - Embedded POSIX fallback vector engine now mirrors the same `.env` and provider-resolution behavior as the primary template.
25
+
26
+ ### Added
27
+ - `mnemo_vector.py` now supports direct CLI operations: `sync`, `search`, `forget`, `health`, and `status`, enabling manual vector workflows outside MCP tool calls.
28
+
9
29
  ## [0.0.2] - 2026-02-21
10
30
 
11
31
  ### Changed
@@ -55,6 +75,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Mnemo u
55
75
  - Version string drift between installer metadata and generated output by using `VERSION` as single source of truth.
56
76
  - Python fallback handling in memory query flows that previously depended on a single interpreter name.
57
77
 
58
- [Unreleased]: https://github.com/DiNaSoR/Mnemo/compare/v0.0.2...HEAD
78
+ [Unreleased]: https://github.com/DiNaSoR/Mnemo/compare/v0.0.4...HEAD
79
+ [0.0.4]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.4
80
+ [0.0.3]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.3
59
81
  [0.0.2]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.2
60
82
  [0.0.1]: https://github.com/DiNaSoR/Mnemo/releases/tag/v0.0.1
package/README.md CHANGED
@@ -138,6 +138,7 @@ You should see MCP tools:
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
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
+ - **Project skill bootstrap**: `.cursor/skills/mnemo-codebase-optimizer/{SKILL.md,reference.md}` for fast codebase-to-memory optimization workflows
141
142
  - **Helper scripts**: `scripts/memory/*` (rebuild, lint, query, add-lesson, add-journal-entry, clear-active)
142
143
  - **Optional SQLite FTS**: `.mnemo/memory/memory.sqlite` when Python is available
143
144
  - **Optional vector layer**: `scripts/memory/mnemo_vector.py` + MCP tools
@@ -191,6 +192,10 @@ Runner triggers:
191
192
  .cursor/ # permanent compatibility bridge
192
193
  memory/
193
194
  rules/
195
+ skills/
196
+ mnemo-codebase-optimizer/
197
+ SKILL.md
198
+ reference.md
194
199
  mcp.json # bridge to .mnemo/mcp/cursor.mcp.json
195
200
 
196
201
  .agent/ # permanent compatibility bridge
@@ -218,7 +223,7 @@ scripts/
218
223
  | `add-lesson.ps1` | Creates next `L-XXX` lesson with normalized tags |
219
224
  | `add-journal-entry.ps1` | Adds entry under current date in monthly journal |
220
225
  | `clear-active.ps1` | Resets `active-context.md` |
221
- | `mnemo_vector.py` | Vector sync/search MCP server (vector mode only) |
226
+ | `mnemo_vector.py` | Vector MCP server + CLI (`sync`, `search`, `forget`, `health`, `status`) in vector mode |
222
227
 
223
228
  ## 🔐 Git hooks and API keys
224
229
 
@@ -230,12 +235,17 @@ Mnemo auto-configures `core.hooksPath` to `.githooks` and installs:
230
235
  Important:
231
236
  - Cursor MCP tools read API keys from `.mnemo/mcp/cursor.mcp.json` env placeholders (`.cursor/mcp.json` stays bridged).
232
237
  - Git hooks read API keys from your shell environment.
238
+ - If `GEMINI_API_KEY` is not already in the environment, `scripts/memory/mnemo_vector.py` also tries loading keys from project-root `.env`.
233
239
 
234
240
  ```sh
235
241
  # bash/zsh example
236
242
  export OPENAI_API_KEY="sk-..."
237
243
  # or
238
244
  export GEMINI_API_KEY="..."
245
+
246
+ # optional direct CLI usage (outside MCP tool calls)
247
+ python3 scripts/memory/mnemo_vector.py sync
248
+ python3 scripts/memory/mnemo_vector.py health
239
249
  ```
240
250
 
241
251
  ## ✅ Recommended daily workflow
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.4
package/memory.ps1 CHANGED
@@ -154,6 +154,7 @@ Write-Host " Query: scripts\memory\query-memory.ps1 -Query ""..."" [-UseS
154
154
  Write-Host " Lint: scripts\memory\lint-memory.ps1" -ForegroundColor DarkGray
155
155
  Write-Host " Clear: scripts\memory\clear-active.ps1" -ForegroundColor DarkGray
156
156
  Write-Host " Rebuild: scripts\memory\rebuild-memory-index.ps1" -ForegroundColor DarkGray
157
+ Write-Host " Skill: .cursor\skills\mnemo-codebase-optimizer\SKILL.md" -ForegroundColor DarkGray
157
158
  Write-Host ""
158
159
 
159
160
  if ($EnableVector -and (-not $DryRun)) {
package/memory_mac.sh CHANGED
@@ -11,7 +11,7 @@
11
11
  # sh ./memory_mac.sh --enable-vector --vector-provider gemini
12
12
  #
13
13
  # This creates:
14
- # .cursor/memory/*, .cursor/rules/*, scripts/memory/*, .githooks/pre-commit
14
+ # .cursor/memory/*, .cursor/rules/*, .cursor/skills/*, scripts/memory/*, .githooks/pre-commit
15
15
  # (and optional .githooks/post-commit when --enable-vector is used)
16
16
 
17
17
  set -eu
@@ -126,6 +126,17 @@ write_file() {
126
126
  printf '%s\n' "WROTE: $path"
127
127
  }
128
128
 
129
+ install_template_file() {
130
+ # install_template_file <template_path> <dest_path>
131
+ template_path="$1"
132
+ dest_path="$2"
133
+ if [ ! -f "$template_path" ]; then
134
+ printf '%s\n' "WARNING: Template not found: $template_path"
135
+ return 0
136
+ fi
137
+ write_file "$dest_path" < "$template_path"
138
+ }
139
+
129
140
  sync_dir_one_way() {
130
141
  src="$1"
131
142
  dst="$2"
@@ -599,6 +610,14 @@ This project uses Mnemo for structured AI memory. All memory lives in `.cursor/m
599
610
  - Clear active-context.md when task is merged
600
611
  EOF
601
612
 
613
+ # -------------------------
614
+ # Cursor project skills
615
+ # -------------------------
616
+
617
+ _skills_tpl_dir="$_INSTALLER_DIR/scripts/memory/installer/templates/skills/mnemo-codebase-optimizer"
618
+ install_template_file "$_skills_tpl_dir/SKILL.md" "$CURSOR_DIR/skills/mnemo-codebase-optimizer/SKILL.md"
619
+ install_template_file "$_skills_tpl_dir/reference.md" "$CURSOR_DIR/skills/mnemo-codebase-optimizer/reference.md"
620
+
602
621
  write_file "$MEM_SCRIPTS_DIR/customization.md" <<'EOF'
603
622
  # Mnemo Memory Customization Prompt (paste into an AI)
604
623
 
@@ -1821,9 +1840,70 @@ from mcp.server.fastmcp import FastMCP
1821
1840
 
1822
1841
  SCHEMA_VERSION = 2
1823
1842
  EMBED_DIM = 1536
1824
- MEM_ROOT = Path(".cursor/memory")
1825
- DB_PATH = MEM_ROOT / "mnemo_vector.sqlite"
1826
- PROVIDER = os.getenv("MNEMO_PROVIDER", "openai").lower()
1843
+
1844
+
1845
+ def _resolve_memory_root() -> Path:
1846
+ override = os.getenv("MNEMO_MEMORY_ROOT", "").strip()
1847
+ if override:
1848
+ return Path(override).expanduser().resolve()
1849
+
1850
+ script_repo = Path(__file__).resolve().parents[2]
1851
+ for rel in ((".mnemo", "memory"), (".cursor", "memory")):
1852
+ candidate = script_repo.joinpath(*rel)
1853
+ if candidate.exists():
1854
+ return candidate
1855
+
1856
+ cwd = Path.cwd().resolve()
1857
+ for root in (cwd, *cwd.parents):
1858
+ for rel in ((".mnemo", "memory"), (".cursor", "memory")):
1859
+ candidate = root.joinpath(*rel)
1860
+ if candidate.exists():
1861
+ return candidate
1862
+ return script_repo / ".mnemo" / "memory"
1863
+
1864
+
1865
+ def _parse_env_line(raw_line: str):
1866
+ line = raw_line.strip()
1867
+ if not line or line.startswith("#"):
1868
+ return None
1869
+ if line.startswith("export "):
1870
+ line = line[7:].strip()
1871
+ if "=" not in line:
1872
+ return None
1873
+ key, value = line.split("=", 1)
1874
+ key = key.strip()
1875
+ if not key or any(ch.isspace() for ch in key):
1876
+ return None
1877
+ value = value.strip()
1878
+ if value and len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
1879
+ value = value[1:-1]
1880
+ elif " #" in value:
1881
+ value = value.split(" #", 1)[0].rstrip()
1882
+ return key, value
1883
+
1884
+
1885
+ def _load_project_env() -> None:
1886
+ if os.getenv("GEMINI_API_KEY"):
1887
+ return
1888
+ env_path = Path(".env")
1889
+ if not env_path.exists():
1890
+ return
1891
+ try:
1892
+ for raw_line in env_path.read_text(encoding="utf-8").splitlines():
1893
+ parsed = _parse_env_line(raw_line)
1894
+ if not parsed:
1895
+ continue
1896
+ key, value = parsed
1897
+ os.environ.setdefault(key, value)
1898
+ except OSError:
1899
+ pass
1900
+
1901
+
1902
+ MEM_ROOT = _resolve_memory_root()
1903
+ _DB_OVERRIDE = os.getenv("MNEMO_DB_PATH", "").strip()
1904
+ DB_PATH = Path(_DB_OVERRIDE).expanduser().resolve() if _DB_OVERRIDE else (MEM_ROOT / "mnemo_vector.sqlite")
1905
+ _load_project_env()
1906
+ PROVIDER = os.getenv("MNEMO_PROVIDER", "gemini" if os.getenv("GEMINI_API_KEY") else "openai").lower()
1827
1907
 
1828
1908
  SKIP_NAMES = {
1829
1909
  "README.md",
@@ -2327,6 +2407,7 @@ GI_END="# <<< Mnemo (generated) >>>"
2327
2407
  ignore_lines=".mnemo/
2328
2408
  .cursor/memory/
2329
2409
  .cursor/rules/
2410
+ .cursor/skills/
2330
2411
  .cursor/mcp.json
2331
2412
  .agent/rules/
2332
2413
  scripts/memory/
@@ -2400,6 +2481,7 @@ fi
2400
2481
  echo ""
2401
2482
  echo "Setup complete. (Mnemo v$MNEMO_VERSION)"
2402
2483
  echo "Next:"
2484
+ echo " skill: .cursor/skills/mnemo-codebase-optimizer/SKILL.md"
2403
2485
  echo " sh ./scripts/memory/rebuild-memory-index.sh"
2404
2486
  echo " sh ./scripts/memory/lint-memory.sh"
2405
2487
  if [ "$ENABLE_VECTOR" = "1" ] && [ "$DRY_RUN" != "1" ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dinasor/mnemo-cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Mnemo installer CLI for the current project folder.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,68 +1,69 @@
1
- <#
2
- gitignore_setup.ps1 - Managed .gitignore block for Mnemo generated artifacts.
3
- Dot-sourced by bootstrap.ps1.
4
- #>
5
-
6
- function Update-MnemoGitignore {
7
- param(
8
- [Parameter(Mandatory=$true)][hashtable]$Ctx,
9
- [switch]$EnableVector
10
- )
11
-
12
- $giPath = Join-Path $Ctx.RepoRoot ".gitignore"
13
- $giBeginMarker = "# >>> Mnemo (generated) - do not edit this block manually <<<"
14
- $giEndMarker = "# <<< Mnemo (generated) >>>"
15
-
16
- # User-facing default: ignore the full Mnemo generated footprint so target repos stay clean by default.
17
- $ignoreLines = @(
18
- ".mnemo/",
19
- ".cursor/memory/",
20
- ".cursor/rules/",
21
- ".cursor/mcp.json",
22
- ".agent/rules/",
23
- "scripts/memory/",
24
- ".githooks/"
25
- )
26
-
27
- $giLineEndings = "CRLF"
28
- $giContent = ""
29
- if (Test-Path $giPath) {
30
- $giContent = Get-Content -Raw -Encoding UTF8 -ErrorAction SilentlyContinue $giPath
31
- if ($null -eq $giContent) { $giContent = "" }
32
- if ($giContent.Length -gt 0 -and [int]$giContent[0] -eq 0xFEFF) { $giContent = $giContent.Substring(1) }
33
- $giLineEndings = if ($giContent -match "`r`n") { "CRLF" } else { "LF" }
34
- }
35
-
36
- $newBlock = $giBeginMarker + "`n" + ($ignoreLines -join "`n") + "`n" + $giEndMarker
37
-
38
- if ($script:DryRun) {
39
- Write-Host "[DRY RUN] WOULD UPDATE: $giPath (managed Mnemo block)" -ForegroundColor DarkCyan
40
- return
41
- }
42
-
43
- $beginEsc = [regex]::Escape($giBeginMarker)
44
- $endEsc = [regex]::Escape($giEndMarker)
45
- $blockPattern = "(?s)$beginEsc.*?$endEsc"
46
-
47
- if ($giContent -match $blockPattern) {
48
- $newContent = [regex]::Replace($giContent, $blockPattern, $newBlock.Replace('$', '$$'))
49
- $existingNorm = $giContent -replace "`r?`n", "`n"
50
- $newNorm = $newContent -replace "`r?`n", "`n"
51
- if ($newNorm -ne $existingNorm) {
52
- $enc = New-Object System.Text.UTF8Encoding($false)
53
- $normalized = if ($giLineEndings -eq "CRLF") { $newContent -replace "`r?`n", "`r`n" } else { $newContent -replace "`r?`n", "`n" }
54
- [System.IO.File]::WriteAllText($giPath, $normalized, $enc)
55
- Write-Host "WROTE: $giPath (updated Mnemo managed block)" -ForegroundColor Green
56
- } else {
57
- Write-Host "SKIP (exists): $giPath (Mnemo managed block unchanged)" -ForegroundColor DarkYellow
58
- }
59
- } else {
60
- $trimmed = $giContent.TrimEnd("`r", "`n")
61
- $sep = if ($trimmed.Length -gt 0) { "`n`n" } else { "" }
62
- $newContent = $trimmed + $sep + $newBlock + "`n"
63
- $enc = New-Object System.Text.UTF8Encoding($false)
64
- $normalized = if ($giLineEndings -eq "CRLF") { $newContent -replace "`r?`n", "`r`n" } else { $newContent -replace "`r?`n", "`n" }
65
- [System.IO.File]::WriteAllText($giPath, $normalized, $enc)
66
- Write-Host "WROTE: $giPath (added Mnemo managed block)" -ForegroundColor Green
67
- }
68
- }
1
+ <#
2
+ gitignore_setup.ps1 - Managed .gitignore block for Mnemo generated artifacts.
3
+ Dot-sourced by bootstrap.ps1.
4
+ #>
5
+
6
+ function Update-MnemoGitignore {
7
+ param(
8
+ [Parameter(Mandatory=$true)][hashtable]$Ctx,
9
+ [switch]$EnableVector
10
+ )
11
+
12
+ $giPath = Join-Path $Ctx.RepoRoot ".gitignore"
13
+ $giBeginMarker = "# >>> Mnemo (generated) - do not edit this block manually <<<"
14
+ $giEndMarker = "# <<< Mnemo (generated) >>>"
15
+
16
+ # User-facing default: ignore the full Mnemo generated footprint so target repos stay clean by default.
17
+ $ignoreLines = @(
18
+ ".mnemo/",
19
+ ".cursor/memory/",
20
+ ".cursor/rules/",
21
+ ".cursor/skills/",
22
+ ".cursor/mcp.json",
23
+ ".agent/rules/",
24
+ "scripts/memory/",
25
+ ".githooks/"
26
+ )
27
+
28
+ $giLineEndings = "CRLF"
29
+ $giContent = ""
30
+ if (Test-Path $giPath) {
31
+ $giContent = Get-Content -Raw -Encoding UTF8 -ErrorAction SilentlyContinue $giPath
32
+ if ($null -eq $giContent) { $giContent = "" }
33
+ if ($giContent.Length -gt 0 -and [int]$giContent[0] -eq 0xFEFF) { $giContent = $giContent.Substring(1) }
34
+ $giLineEndings = if ($giContent -match "`r`n") { "CRLF" } else { "LF" }
35
+ }
36
+
37
+ $newBlock = $giBeginMarker + "`n" + ($ignoreLines -join "`n") + "`n" + $giEndMarker
38
+
39
+ if ($script:DryRun) {
40
+ Write-Host "[DRY RUN] WOULD UPDATE: $giPath (managed Mnemo block)" -ForegroundColor DarkCyan
41
+ return
42
+ }
43
+
44
+ $beginEsc = [regex]::Escape($giBeginMarker)
45
+ $endEsc = [regex]::Escape($giEndMarker)
46
+ $blockPattern = "(?s)$beginEsc.*?$endEsc"
47
+
48
+ if ($giContent -match $blockPattern) {
49
+ $newContent = [regex]::Replace($giContent, $blockPattern, $newBlock.Replace('$', '$$'))
50
+ $existingNorm = $giContent -replace "`r?`n", "`n"
51
+ $newNorm = $newContent -replace "`r?`n", "`n"
52
+ if ($newNorm -ne $existingNorm) {
53
+ $enc = New-Object System.Text.UTF8Encoding($false)
54
+ $normalized = if ($giLineEndings -eq "CRLF") { $newContent -replace "`r?`n", "`r`n" } else { $newContent -replace "`r?`n", "`n" }
55
+ [System.IO.File]::WriteAllText($giPath, $normalized, $enc)
56
+ Write-Host "WROTE: $giPath (updated Mnemo managed block)" -ForegroundColor Green
57
+ } else {
58
+ Write-Host "SKIP (exists): $giPath (Mnemo managed block unchanged)" -ForegroundColor DarkYellow
59
+ }
60
+ } else {
61
+ $trimmed = $giContent.TrimEnd("`r", "`n")
62
+ $sep = if ($trimmed.Length -gt 0) { "`n`n" } else { "" }
63
+ $newContent = $trimmed + $sep + $newBlock + "`n"
64
+ $enc = New-Object System.Text.UTF8Encoding($false)
65
+ $normalized = if ($giLineEndings -eq "CRLF") { $newContent -replace "`r?`n", "`r`n" } else { $newContent -replace "`r?`n", "`n" }
66
+ [System.IO.File]::WriteAllText($giPath, $normalized, $enc)
67
+ Write-Host "WROTE: $giPath (added Mnemo managed block)" -ForegroundColor Green
68
+ }
69
+ }
@@ -436,6 +436,16 @@ function Install-MemoryScripts {
436
436
  -ForceWrite:$Force
437
437
  }
438
438
 
439
+ $cursorSkillTemplates = @("SKILL.md", "reference.md")
440
+ foreach ($f in $cursorSkillTemplates) {
441
+ Install-TemplateFile `
442
+ -TemplateName "skills\mnemo-codebase-optimizer\$f" `
443
+ -DestPath (Join-Path $Ctx.CursorDir "skills\mnemo-codebase-optimizer\$f") `
444
+ -InstallerRoot $InstallerRoot `
445
+ -LineEndings "LF" `
446
+ -ForceWrite:$Force
447
+ }
448
+
439
449
  if ($EnableVector) {
440
450
  Install-TemplateFile `
441
451
  -TemplateName "mnemo_vector.py" `
@@ -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 cwd / ".mnemo" / "memory"
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()
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: mnemo-codebase-optimizer
3
+ description: Builds high-signal Mnemo memory quickly for any codebase by mapping architecture, ownership, workflows, risks, and commands, then writing optimized memo/hot-rules/active-context/lessons/journal entries and validating retrieval quality. Use when users ask to bootstrap Mnemo memory, optimize project context, or fill memory in a new or existing repository.
4
+ ---
5
+
6
+ # Mnemo Codebase Optimizer
7
+
8
+ ## Purpose
9
+
10
+ Use this skill to rapidly produce a detailed, reliable Mnemo knowledge base for any project.
11
+ The goal is high retrieval quality with low token waste.
12
+
13
+ ## Quick Start
14
+
15
+ Use this checklist and keep one step `in_progress` at a time:
16
+
17
+ ```text
18
+ Memory Seeding Progress
19
+ - [ ] 1) Confirm Mnemo install + paths
20
+ - [ ] 2) Map codebase architecture and ownership
21
+ - [ ] 3) Extract runbook commands and dev workflows
22
+ - [ ] 4) Write/update core memory files
23
+ - [ ] 5) Add initial lessons and journal summary
24
+ - [ ] 6) Rebuild + lint + (optional) vector sync
25
+ - [ ] 7) Validate retrieval quality and refine
26
+ ```
27
+
28
+ ## 1) Confirm Mnemo Install
29
+
30
+ Verify these paths exist:
31
+ - `.mnemo/memory/`
32
+ - `.mnemo/rules/`
33
+ - `scripts/memory/`
34
+
35
+ If missing, run installer first and stop this skill until setup succeeds.
36
+
37
+ ## 2) Map Architecture Fast
38
+
39
+ Collect only high-value context:
40
+ - product purpose and core user flows
41
+ - entrypoints and runtime boundaries (frontend/backend/workers/CLI)
42
+ - module ownership and key responsibilities
43
+ - data model sources and contracts
44
+ - integration surfaces (APIs, queues, webhooks, external services)
45
+ - quality/security constraints and release workflow
46
+
47
+ Avoid copying large code blocks into memory files. Summarize and reference paths.
48
+
49
+ ## 3) Capture Operational Workflow
50
+
51
+ Record:
52
+ - install/build/test/lint commands
53
+ - local run commands for each app/service
54
+ - deployment/release commands and prerequisites
55
+ - debugging commands and common failure signatures
56
+
57
+ Prefer explicit command examples over vague prose.
58
+
59
+ ## 4) Write Core Memory Files
60
+
61
+ Update these files in priority order:
62
+
63
+ 1. `.mnemo/memory/hot-rules.md`
64
+ - 10-20 lines max
65
+ - only hard invariants and "never do" rules
66
+ 2. `.mnemo/memory/memo.md`
67
+ - architecture map, ownership, critical paths, runbook
68
+ 3. `.mnemo/memory/active-context.md`
69
+ - current migration task + open risks + next actions
70
+
71
+ Use short sections and bullets. Keep each bullet atomic and testable.
72
+
73
+ ## 5) Seed Lessons + Journal
74
+
75
+ Create 3-8 starter lessons from known pitfalls:
76
+ - naming conventions
77
+ - data migration gotchas
78
+ - environment/setup traps
79
+ - release regressions and prevention checks
80
+
81
+ Add one monthly journal entry summarizing:
82
+ - what memory was seeded
83
+ - why those sections matter
84
+ - what remains unknown
85
+
86
+ ## 6) Rebuild and Validate
87
+
88
+ Run:
89
+ - `scripts/memory/rebuild-memory-index.ps1` (or shell equivalent)
90
+ - `scripts/memory/lint-memory.ps1` (or shell equivalent)
91
+
92
+ If vector mode is enabled:
93
+ - run `vector_sync`
94
+ - run `vector_health`
95
+
96
+ ## 7) Retrieval Quality Gate
97
+
98
+ Ask 5-10 realistic queries and verify results:
99
+ - architecture query (module boundaries)
100
+ - workflow query (how to run/test/release)
101
+ - ownership query (who owns what)
102
+ - pitfall query (common mistakes)
103
+ - troubleshooting query (known failure/fix)
104
+
105
+ If retrieval misses:
106
+ - tighten memo headings
107
+ - split overloaded bullets
108
+ - add a lesson instead of bloating memo
109
+ - prune low-signal historical text
110
+
111
+ ## Memory Quality Rules
112
+
113
+ - Keep `hot-rules.md` tiny and immutable-focused.
114
+ - Keep `memo.md` as current truth, not raw history.
115
+ - Put historical detail in journal, not memo.
116
+ - Prefer lessons for reusable error-prevention patterns.
117
+ - Use stable terminology for search consistency.
118
+ - Every important claim should map to concrete file paths or commands.
119
+
120
+ ## Output Contract
121
+
122
+ When done, return:
123
+
124
+ 1. **Coverage summary**
125
+ - what domains are now represented in memory
126
+ 2. **Files changed**
127
+ - list of memory files updated/created
128
+ 3. **Quality checks**
129
+ - rebuild/lint/vector status
130
+ 4. **Top retrieval queries**
131
+ - sample queries and expected hit files
132
+ 5. **Remaining unknowns**
133
+ - explicit gaps to fill in next pass
134
+
135
+ ## Additional Resources
136
+
137
+ - Detailed templates: [reference.md](reference.md)
@@ -0,0 +1,138 @@
1
+ # Mnemo Codebase Optimizer Reference
2
+
3
+ ## Memo Template (Detailed)
4
+
5
+ Use this structure for `.mnemo/memory/memo.md`:
6
+
7
+ ```markdown
8
+ # Project Memo - <project>
9
+
10
+ ## Mission
11
+ - One-paragraph purpose and primary user outcomes.
12
+
13
+ ## System Shape
14
+ - Runtime components:
15
+ - `<component>`: `<responsibility>`
16
+ - Boundaries:
17
+ - `<boundary>` -> `<dependency>`
18
+
19
+ ## Ownership Map
20
+ - `<module/path>` -> `<team/owner>`
21
+ - `<module/path>` -> `<team/owner>`
22
+
23
+ ## Critical Flows
24
+ - Flow A:
25
+ - Trigger:
26
+ - Core path:
27
+ - Side effects:
28
+ - Failure modes:
29
+ - Flow B:
30
+ - Trigger:
31
+ - Core path:
32
+ - Side effects:
33
+ - Failure modes:
34
+
35
+ ## Data Contracts
36
+ - `<type/schema/file>`: purpose + key fields + constraints
37
+ - `<type/schema/file>`: purpose + key fields + constraints
38
+
39
+ ## Commands Runbook
40
+ - Install: `<command>`
41
+ - Dev run: `<command>`
42
+ - Tests: `<command>`
43
+ - Lint/format: `<command>`
44
+ - Build/release: `<command>`
45
+
46
+ ## Constraints and Guardrails
47
+ - Non-negotiables:
48
+ - `<rule>`
49
+ - `<rule>`
50
+ - Security/compliance:
51
+ - `<constraint>`
52
+
53
+ ## Known Risks
54
+ - `<risk>` -> detection -> mitigation
55
+ - `<risk>` -> detection -> mitigation
56
+ ```
57
+
58
+ ## Hot Rules Template
59
+
60
+ Use this structure for `.mnemo/memory/hot-rules.md` (10-20 lines):
61
+
62
+ ```markdown
63
+ # Hot Rules
64
+
65
+ - Never bypass `<critical check>`.
66
+ - Do not edit `<generated path>` manually.
67
+ - Keep `<artifact>` synchronized with `<source>`.
68
+ - Before release: run `<required command list>`.
69
+ - If `<failure condition>`, stop and verify `<source of truth>`.
70
+ ```
71
+
72
+ ## Active Context Template
73
+
74
+ Use this structure for `.mnemo/memory/active-context.md`:
75
+
76
+ ```markdown
77
+ # Active Context
78
+
79
+ ## Current Task
80
+ - Goal:
81
+ - Scope:
82
+ - Out of scope:
83
+
84
+ ## Working Notes
85
+ - Current status:
86
+ - Decisions made:
87
+ - Risks and blockers:
88
+
89
+ ## Next Actions
90
+ 1. `<action>`
91
+ 2. `<action>`
92
+ 3. `<action>`
93
+ ```
94
+
95
+ ## Starter Lesson Template
96
+
97
+ Use this for each `L-XXX-*.md`:
98
+
99
+ ```markdown
100
+ ---
101
+ id: L-XXX
102
+ title: "<short pitfall title>"
103
+ tags: ["pitfall", "regression", "<domain>"]
104
+ applies_to: ["<path/glob>"]
105
+ rule: "<single actionable rule>"
106
+ ---
107
+
108
+ ## Context
109
+ When this appears and why it happens.
110
+
111
+ ## Failure Pattern
112
+ Observable signs and likely root causes.
113
+
114
+ ## Corrective Action
115
+ Exact steps to fix safely.
116
+
117
+ ## Prevention Check
118
+ How to detect it before merge/release.
119
+ ```
120
+
121
+ ## Retrieval Validation Query Set
122
+
123
+ Run and confirm each query returns high-signal files:
124
+
125
+ 1. "What are the core runtime boundaries?"
126
+ 2. "Which files define the main data contracts?"
127
+ 3. "How do I run full tests and lint before release?"
128
+ 4. "What regressions happened before and how do we prevent them?"
129
+ 5. "Who owns `<critical module>` and what are the rules?"
130
+ 6. "What are the top production risks in this repo?"
131
+
132
+ ## Optimization Heuristics
133
+
134
+ - Prefer one clear heading over long paragraphs.
135
+ - Replace duplicate bullets with one canonical bullet.
136
+ - If a memo section grows too long, split into lessons.
137
+ - Keep journaling chronological; keep memo conceptual.
138
+ - Remove stale TODOs from memo; keep only current truth.