# setup.ps1 — install mem on Windows (latent-memory daily journal) # # One-liner (PowerShell): # irm https://raw.githubusercontent.com/karmugilen/mem/main/setup.ps1 | iex # # From a clone: # .\setup.ps1 # .\setup.ps1 -DataDir "$env:USERPROFILE\Notes\mem" -NoAgents # #Requires -Version 5.1 [CmdletBinding()] param( [string] $DataDir = $(if ($env:MEM_HOME) { $env:MEM_HOME } else { Join-Path $env:USERPROFILE "mem" }), [string] $BinDir = $(if ($env:MEM_BIN_DIR) { $env:MEM_BIN_DIR } else { Join-Path $env:USERPROFILE ".local\bin" }), [string] $InstallDir = $(if ($env:MEM_INSTALL_DIR) { $env:MEM_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA "mem-tool" }), [string] $RepoUrl = $(if ($env:MEM_REPO_URL) { $env:MEM_REPO_URL } else { "https://github.com/karmugilen/mem.git" }), [switch] $NoAgents, [switch] $AgentsOnly, [switch] $NoClone ) $ErrorActionPreference = "Stop" function Write-Step([string] $Message) { Write-Host "==> $Message" } function Write-Warn([string] $Message) { Write-Warning $Message } function Die([string] $Message) { Write-Error "error: $Message"; exit 1 } function Test-Command([string] $Name) { return [bool](Get-Command $Name -ErrorAction SilentlyContinue) } function Get-Python { foreach ($c in @("py", "python", "python3")) { if (Test-Command $c) { try { if ($c -eq "py") { $v = & py -3 -c "import sys; print(sys.version_info >= (3, 8))" 2>$null if ($v -match "True") { return @("py", "-3") } } else { $v = & $c -c "import sys; print(sys.version_info >= (3, 8))" 2>$null if ($v -match "True") { return @($c) } } } catch { } } } Die "Python 3.8+ not found. Install from https://www.python.org/downloads/ and tick 'Add python.exe to PATH'." } function Get-SourceDir { # When run from a real file in a clone if ($PSScriptRoot -and (Test-Path (Join-Path $PSScriptRoot "bin\mem"))) { return $PSScriptRoot } if ($NoClone) { Die "bin\mem not found next to setup.ps1 (use a full clone, or drop -NoClone)" } if (-not (Test-Command "git")) { Die "git not found. Install from https://git-scm.com/" } if (Test-Path (Join-Path $InstallDir "bin\mem")) { Write-Step "updating existing install at $InstallDir" try { git -C $InstallDir pull --ff-only 2>$null } catch { } return $InstallDir } Write-Step "cloning $RepoUrl → $InstallDir" $parent = Split-Path $InstallDir -Parent if ($parent -and -not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } if (Test-Path $InstallDir) { Remove-Item $InstallDir -Recurse -Force } git clone --depth 1 $RepoUrl $InstallDir return $InstallDir } function Ensure-UserPath([string] $Dir) { $userPath = [Environment]::GetEnvironmentVariable("Path", "User") if (-not $userPath) { $userPath = "" } $parts = $userPath -split ";" | Where-Object { $_ -ne "" } if ($parts -contains $Dir) { Write-Step "PATH already includes $Dir" return } $newPath = if ($userPath.Trim()) { "$Dir;$userPath" } else { $Dir } [Environment]::SetEnvironmentVariable("Path", $newPath, "User") $env:Path = "$Dir;$env:Path" Write-Step "added $Dir to your user PATH (new terminals will see it)" } function Install-Agents([string] $Src, [string] $Data) { $tpl = Join-Path $Src "templates" if (-not (Test-Path $tpl)) { Write-Warn "no templates/ in $Src — skip agents" return } $claudeDir = Join-Path $env:USERPROFILE ".claude" $claudeMd = Join-Path $claudeDir "CLAUDE.md" $skillDir = Join-Path $claudeDir "skills\mem" New-Item -ItemType Directory -Path $skillDir -Force | Out-Null $snippet = Join-Path $tpl "CLAUDE.md.snippet" if (Test-Path $snippet) { $text = Get-Content $snippet -Raw -Encoding UTF8 if ((Test-Path $claudeMd) -and ((Get-Content $claudeMd -Raw -ErrorAction SilentlyContinue) -match "mem last 7|Journal memory \(mem\)|daily journal memory")) { Write-Step "Claude instructions already present in $claudeMd" } elseif (Test-Path $claudeMd) { Add-Content -Path $claudeMd -Value "`n`n$text" -Encoding UTF8 Write-Step "appended mem instructions to $claudeMd" } else { New-Item -ItemType Directory -Path $claudeDir -Force | Out-Null Set-Content -Path $claudeMd -Value $text -Encoding UTF8 Write-Step "wrote $claudeMd" } } $skill = Join-Path $tpl "SKILL.md" if (Test-Path $skill) { Copy-Item $skill (Join-Path $skillDir "SKILL.md") -Force Write-Step "installed Claude skill → $skillDir\SKILL.md" } $grokRoot = Join-Path $env:USERPROFILE ".grok" if (Test-Path $grokRoot) { $grokSkill = Join-Path $grokRoot "skills\mem" New-Item -ItemType Directory -Path $grokSkill -Force | Out-Null if (Test-Path $skill) { Copy-Item $skill (Join-Path $grokSkill "SKILL.md") -Force Write-Step "installed Grok skill → $grokSkill\SKILL.md" } } $agents = Join-Path $tpl "AGENTS.md" if (Test-Path $agents) { Copy-Item $agents (Join-Path $Data "AGENTS.md") -Force Write-Step "wrote $(Join-Path $Data 'AGENTS.md')" } $dataReadme = Join-Path $tpl "DATA_README.md" if (Test-Path $dataReadme) { Copy-Item $dataReadme (Join-Path $Data "README.md") -Force } } # --- main ------------------------------------------------------------------- $py = Get-Python Write-Step ("using Python: " + ($py -join " ")) if ($AgentsOnly) { $src = Get-SourceDir Install-Agents $src $DataDir Write-Step "done (agents only)" exit 0 } $src = Get-SourceDir $memPy = Join-Path $src "bin\mem" if (-not (Test-Path $memPy)) { Die "bin\mem missing in $src" } Write-Step "data home: $DataDir" Write-Step "tool source: $src" Write-Step "bin shim: $(Join-Path $BinDir 'mem.cmd')" New-Item -ItemType Directory -Path (Join-Path $DataDir "journal") -Force | Out-Null New-Item -ItemType Directory -Path $BinDir -Force | Out-Null # git autocommit root if (-not (Test-Path (Join-Path $DataDir ".git"))) { if (-not (Test-Command "git")) { Die "git not found. Install from https://git-scm.com/" } git -C $DataDir init -q @" .obsidian/ .DS_Store *~ *.swp Thumbs.db Desktop.ini "@ | Set-Content (Join-Path $DataDir ".gitignore") -Encoding UTF8 git -C $DataDir add -A try { git -C $DataDir commit -q -m "mem: initial journal" } catch { } Write-Step "initialized git repo in $DataDir (autocommit on every write)" } else { Write-Step "git repo already present in $DataDir" } # mem.cmd shim — works in cmd.exe and PowerShell $shim = Join-Path $BinDir "mem.cmd" $pyExe = $py[0] $pyArgs = if ($py.Count -gt 1) { ($py[1..($py.Count - 1)] -join " ") + " " } else { "" } # Use absolute path to mem script; %* forwards all args @" @echo off REM mem — latent-memory journal CLI (installed by setup.ps1) set "MEM_HOME=$DataDir" $pyExe ${pyArgs}"$memPy" %* "@ | Set-Content $shim -Encoding ASCII Write-Step "wrote $shim" Ensure-UserPath $BinDir # document non-default data dir $defaultData = Join-Path $env:USERPROFILE "mem" if ($DataDir -ne $defaultData) { @" # PowerShell — add to your profile if needed: `$env:MEM_HOME = '$DataDir' "@ | Set-Content (Join-Path $DataDir "env.ps1") -Encoding UTF8 Write-Step "non-default data dir — MEM_HOME is set inside mem.cmd; for other shells set:" Write-Host " `$env:MEM_HOME = '$DataDir'" } if (-not $NoAgents) { Install-Agents $src $DataDir } $env:MEM_HOME = $DataDir $env:Path = "$BinDir;$env:Path" function Invoke-Mem([Parameter(ValueFromRemainingArguments = $true)][string[]] $MemArgs) { if ($py.Count -ge 2) { & $py[0] $py[1] $memPy @MemArgs } else { & $py[0] $memPy @MemArgs } } Write-Step "verifying..." Invoke-Mem help | Out-Null if ($LASTEXITCODE -ne 0) { Die "mem help failed" } Invoke-Mem path Invoke-Mem log "mem: installed — ready to use as latent memory" Write-Host "" Write-Host "✓ mem is installed (Windows)" Write-Host "" Write-Host " Journal: $(Join-Path $DataDir 'journal')" Write-Host " CLI: $shim" Write-Host " Source: $src" Write-Host "" Write-Host "Open a NEW terminal, then:" Write-Host " mem log `"myapp: got auth working`"" Write-Host " mem log `"myapp: decided: SQLite over Postgres — single-user desktop app`"" Write-Host " mem task `"myapp: fix JWT expiry in auth.py`"" Write-Host " mem last 7" Write-Host " mem search decided:" Write-Host "" Write-Host "Built on the latent memory concept: tiny MD cues, day-wise, shared" Write-Host "across projects — far fewer tokens than RAG or chat dumps." Write-Host "" Write-Host "Open $DataDir in Obsidian as a vault if you want a GUI." Write-Host "Agent tip: in a new AI session, run or ask for: mem last 7"