@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 +23 -1
- package/README.md +11 -1
- package/VERSION +1 -1
- package/memory.ps1 +1 -0
- package/memory_mac.sh +86 -4
- package/package.json +1 -1
- package/scripts/memory/installer/features/gitignore_setup.ps1 +69 -68
- package/scripts/memory/installer/features/memory_scaffold.ps1 +10 -0
- package/scripts/memory/installer/templates/mnemo_vector.py +100 -2
- package/scripts/memory/installer/templates/skills/mnemo-codebase-optimizer/SKILL.md +137 -0
- package/scripts/memory/installer/templates/skills/mnemo-codebase-optimizer/reference.md +138 -0
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.
|
|
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
|
|
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.
|
|
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
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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,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/
|
|
22
|
-
".
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
$
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if ($
|
|
33
|
-
$
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
$
|
|
45
|
-
$
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
$
|
|
50
|
-
$
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
$
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
$
|
|
62
|
-
$
|
|
63
|
-
$
|
|
64
|
-
$
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
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.
|