cli-mem 0.3.6__tar.gz → 0.4.0__tar.gz

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.

Potentially problematic release.


This version of cli-mem might be problematic. Click here for more details.

Files changed (50) hide show
  1. {cli_mem-0.3.6 → cli_mem-0.4.0}/ARCHITECTURE.md +156 -26
  2. {cli_mem-0.3.6 → cli_mem-0.4.0}/PHILOSOPHY.md +7 -4
  3. cli_mem-0.4.0/PKG-INFO +410 -0
  4. cli_mem-0.4.0/README.md +381 -0
  5. cli_mem-0.4.0/command-groups/databases/postgres-debug.json +31 -0
  6. cli_mem-0.4.0/command-groups/databases/redis-ops.json +31 -0
  7. cli_mem-0.4.0/command-groups/devops/docker-cleanup.json +31 -0
  8. cli_mem-0.4.0/command-groups/devops/incident-response.json +31 -0
  9. cli_mem-0.4.0/command-groups/devops/k8s-debug.json +35 -0
  10. cli_mem-0.4.0/command-groups/git/cleanup.json +31 -0
  11. cli_mem-0.4.0/command-groups/git/forensics.json +35 -0
  12. cli_mem-0.4.0/command-groups/networking/dns-debug.json +35 -0
  13. cli_mem-0.4.0/command-groups/networking/network-debug.json +31 -0
  14. cli_mem-0.4.0/command-groups/performance/system-profile.json +35 -0
  15. cli_mem-0.4.0/command-groups/security/headers-audit.json +31 -0
  16. cli_mem-0.4.0/command-groups/security/recon.json +35 -0
  17. cli_mem-0.4.0/command-groups/security/ssl-audit.json +31 -0
  18. cli_mem-0.4.0/docs/decisions/003-no-daemon.md +34 -0
  19. cli_mem-0.4.0/hooks/mem.bash +42 -0
  20. cli_mem-0.4.0/hooks/mem.fish +13 -0
  21. {cli_mem-0.3.6 → cli_mem-0.4.0}/pyproject.toml +1 -1
  22. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/__init__.py +1 -1
  23. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/cli.py +176 -32
  24. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/groups.py +56 -16
  25. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/conftest.py +1 -3
  26. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/test_groups.py +309 -1
  27. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/test_patterns.py +1 -3
  28. cli_mem-0.3.6/PKG-INFO +0 -315
  29. cli_mem-0.3.6/README.md +0 -286
  30. cli_mem-0.3.6/docs/decisions/003-no-daemon.md +0 -25
  31. {cli_mem-0.3.6 → cli_mem-0.4.0}/.github/workflows/ci.yml +0 -0
  32. {cli_mem-0.3.6 → cli_mem-0.4.0}/.github/workflows/release.yml +0 -0
  33. {cli_mem-0.3.6 → cli_mem-0.4.0}/.gitignore +0 -0
  34. {cli_mem-0.3.6 → cli_mem-0.4.0}/LICENSE +0 -0
  35. {cli_mem-0.3.6 → cli_mem-0.4.0}/assets/.gitkeep +0 -0
  36. {cli_mem-0.3.6 → cli_mem-0.4.0}/docs/decisions/001-jsonl-over-sqlite.md +0 -0
  37. {cli_mem-0.3.6 → cli_mem-0.4.0}/docs/decisions/002-apple-fm-sdk-for-patterns.md +0 -0
  38. {cli_mem-0.3.6 → cli_mem-0.4.0}/docs/decisions/004-per-repo-jsonl.md +0 -0
  39. {cli_mem-0.3.6 → cli_mem-0.4.0}/hooks/mem.zsh +0 -0
  40. {cli_mem-0.3.6 → cli_mem-0.4.0}/install.sh +0 -0
  41. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/_generable.py +0 -0
  42. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/capture.py +0 -0
  43. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/models.py +0 -0
  44. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/patterns.py +0 -0
  45. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/search.py +0 -0
  46. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/storage.py +0 -0
  47. {cli_mem-0.3.6 → cli_mem-0.4.0}/src/mem/variables.py +0 -0
  48. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/test_capture.py +0 -0
  49. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/test_search.py +0 -0
  50. {cli_mem-0.3.6 → cli_mem-0.4.0}/tests/test_storage.py +0 -0
@@ -17,12 +17,15 @@ Technical overview of how mem works under the hood.
17
17
  ┌─────────────────────────────────────────────────────────┐
18
18
  │ src/mem/cli.py │
19
19
  │ │
20
- │ mem <query> mem sync mem session │
21
- │ mem init <shell> mem stats mem forget │
22
- │ mem _capture (hidden) │
23
- └───┬────────────┬──────────────┬──────────────┬──────────┘
24
- │ │ │ │
25
- ▼ ▼ ▼ ▼
20
+ │ mem <query> mem save mem run │
21
+ │ mem list mem session mem stats │
22
+ │ mem forget mem export mem import │
23
+ │ mem group * mem saved * mem vars * │
24
+ │ mem init <shell> │
25
+ │ mem _capture (hidden) mem _sync (hidden) │
26
+ └───┬────────────┬──────────┬──────────┬──────────────────┘
27
+ │ │ │ │
28
+ ▼ ▼ ▼ ▼
26
29
  ┌────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────────┐
27
30
  │capture │ │ search │ │ patterns │ │ storage │
28
31
  │ .py │ │ .py │ │ .py │ │ .py │
@@ -30,19 +33,36 @@ Technical overview of how mem works under the hood.
30
33
  │ hook │ │ score │ │ Apple FM │ │ JSONL read/write │
31
34
  │ capture│ │ rank │ │ SDK │ │ JSON read/write │
32
35
  │ session│ │ filter │ │ guided │ │ rotate / forget │
33
- │ tracker│ │ dedup │ │ gen │ │ │
36
+ │ tracker│ │ dedup │ │ gen │ │ groups / vars │
37
+ │ auto- │ │ │ │ caching │ │ │
38
+ │ sync │ │ │ │ │ │ │
34
39
  └───┬────┘ └────┬────┘ └────┬─────┘ └────────┬──────────┘
35
40
  │ │ │ │
36
- └───────────┴───────────┴─────────────────┘
41
+ ▼ ▼ ▼ ▼
42
+ ┌────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────────┐
43
+ │groups │ │variables│ │_generable│ │ models.py │
44
+ │ .py │ │ .py │ │ .py │ │ │
45
+ │ │ │ │ │ │ │ Pydantic schemas │
46
+ │ save │ │ parse │ │ @fm. │ │ CapturedCommand │
47
+ │ list │ │ resolve │ │ generable│ │ WorkSession │
48
+ │ export │ │ detect │ │ types │ │ GroupFile / Group │
49
+ │ import │ │ credent.│ │ │ │ VarsFile │
50
+ │ scope │ │ store │ │ │ │ PatternFile │
51
+ └────────┘ └─────────┘ └──────────┘ └──────────────────┘
37
52
  │
38
53
  ▼
39
54
  ┌─────────────────────────────────────────────────────────┐
40
- │ ~/.mem/ (filesystem) │
55
+ │ ~/.mem/ (filesystem) │
41
56
  │ │
42
- │ repos/ sessions/ patterns/ │
57
+ │ repos/ sessions/ patterns/ │
43
58
  │ myapp.jsonl 2026-03-05.jsonl kubectl.json │
44
59
  │ infra.jsonl 2026-03-06.jsonl docker.json │
45
60
  │ _global.jsonl git.json │
61
+ │ │
62
+ │ groups/ │
63
+ │ repos/ │
64
+ │ myapp.json vars.json .sync_counter │
65
+ │ _global.json .session_state.json │
46
66
  └─────────────────────────────────────────────────────────┘
47
67
  ```
48
68
 
@@ -76,6 +96,11 @@ capture.SessionTracker.update()
76
96
  │ checks idle time and repo change
77
97
  │ closes session if boundary detected
78
98
  ▼
99
+ Auto-sync check
100
+ │ increments capture counter
101
+ │ every 20 captures: spawns `mem _sync` as detached background process
102
+ │ └── pattern extraction + data rotation, fully silent
103
+ ▼
79
104
  Done (total overhead: <5ms added to prompt)
80
105
  ```
81
106
 
@@ -112,24 +137,32 @@ Deduplicate by command string (keep highest score)
112
137
  Return top N sorted by score descending
113
138
  ```
114
139
 
115
- ### Pattern Extraction
140
+ ### Automatic Pattern Extraction
116
141
 
117
142
  ```
118
- User runs: mem sync
143
+ Every 20 captured commands:
119
144
  │
120
145
  ▼
121
- patterns.sync_all_patterns()
146
+ capture.py spawns `mem _sync` as detached background process
147
+ │ subprocess.Popen with start_new_session=True
148
+ │ no stdout, no stderr — completely invisible
149
+ ▼
150
+ patterns.sync_all_patterns(silent=True)
122
151
  │ reads ALL commands from all repo files
123
152
  │ groups by tool (first token)
124
153
  │ skips tools with <5 commands
125
154
  │
126
155
  ▼ (for each tool with enough data)
127
156
  patterns.extract_patterns_for_tool()
157
+ │
158
+ ├── Load cached processed_commands from existing PatternFile
159
+ ├── Skip already-processed commands (cache hit)
128
160
  │
129
161
  ├── If apple-fm-sdk available:
130
- │ │ builds prompt with command list
131
- │ │ calls Apple FM with Pydantic guided generation
132
- │ └── returns PatternExtractionResult
162
+ │ │ generalize each NEW unique command individually
163
+ │ │ └── fresh LanguageModelSession per command (context safety)
164
+ │ │ merge with cached generalizations
165
+ │ └── aggregate frequencies by pattern (code)
133
166
  │
134
167
  └── If not available:
135
168
  │ groups identical commands
@@ -138,16 +171,78 @@ patterns.extract_patterns_for_tool()
138
171
  ▼
139
172
  storage.write_patterns()
140
173
  │ writes to ~/.mem/patterns/<tool>.json
174
+ │ includes processed_commands list for caching
141
175
  │ atomic: write tmp file, then rename
142
176
  ▼
143
177
  storage.rotate()
144
178
  │ removes commands older than 90 days
145
179
  │ deletes session files older than 30 days
146
- │ NEVER touches patterns/
180
+ │ NEVER touches patterns/ (accumulated learning)
181
+ ▼
182
+ Done (user never sees any of this)
183
+ ```
184
+
185
+ ### Saving Commands with Variables
186
+
187
+ ```
188
+ User runs: mem save "curl -H 'Bearer eyJhbG...' https://api.example.com" -g api
189
+ │
190
+ ▼
191
+ cli.save()
192
+ │ interactive terminal detected
193
+ ▼
194
+ variables.detect_credentials(cmd)
195
+ │
196
+ ├── _command_may_contain_credentials() — heuristic pre-filter
197
+ │ checks for credential keywords + long tokens (≥16 chars)
198
+ │ skips simple commands (echo, ls, cd) entirely
199
+ │
200
+ ├── If pre-filter passes and apple-fm-sdk available:
201
+ │ │ _detect_credentials_async() via Apple FM
202
+ │ │ └── guided generation with CredentialList generable
203
+ │ │
204
+ │ └── _deduplicate_detections() — post-filter
205
+ │ removes: hallucinations, URLs, hostnames, short values, subsets
206
+ │ extracts: actual secret from --flag=value syntax
207
+ │ normalizes: CamelCase → UPPER_SNAKE_CASE
208
+ │
209
+ └── If SDK unavailable: returns empty list (save proceeds normally)
210
+ │
211
+ ▼
212
+ User confirms/renames each detected credential
213
+ │ command text updated with $VAR_NAME tokens
214
+ ▼
215
+ variables.parse_variables() — detect $VAR_NAME tokens
216
+ variables.merge_var_declarations() — merge with --var flags
217
+ ▼
218
+ groups.save_command()
219
+ │ stores command structure + var declarations (never values)
147
220
  ▼
148
221
  Done
149
222
  ```
150
223
 
224
+ ### Running Commands with Variables
225
+
226
+ ```
227
+ User runs: mem run api API_TOKEN=abc123
228
+ │
229
+ ▼
230
+ Parse inline VAR=VALUE arguments
231
+ ▼
232
+ Resolve all variables upfront (before any execution):
233
+ 1. Inline arguments ← highest priority
234
+ 2. Shell environment (os.environ)
235
+ 3. Persistent store (~/.mem/vars.json)
236
+ 4. Default value (from --var at save time)
237
+ 5. Interactive prompt ← last resort
238
+ ▼
239
+ Display resolution summary
240
+ ✓ $API_TOKEN resolved from arguments
241
+ ✓ $NAMESPACE resolved from default
242
+ ▼
243
+ Execute commands with substituted values
244
+ ```
245
+
151
246
  ## JSONL Schemas
152
247
 
153
248
  ### Command Entry (`repos/<repo>.jsonl`)
@@ -157,7 +252,7 @@ Done
157
252
  "command": "kubectl rollout restart deployment api",
158
253
  "ts": 1709600000,
159
254
  "dir": "/Users/mati/projects/services/api",
160
- "repo": "services",
255
+ "repo": "/Users/mati/projects/services/api",
161
256
  "exit_code": 0,
162
257
  "duration_ms": 312,
163
258
  "session": "abc123def456"
@@ -173,7 +268,7 @@ Done
173
268
  "started_at": 1709599500,
174
269
  "ended_at": 1709600400,
175
270
  "dir": "/Users/mati/projects/services/api",
176
- "repo": "services",
271
+ "repo": "/Users/mati/projects/services/api",
177
272
  "commands": [
178
273
  "kubectl logs api-7f9b --tail=100",
179
274
  "kubectl get pods -n production",
@@ -192,14 +287,49 @@ Done
192
287
  "pattern": "kubectl get <resource>",
193
288
  "example": "kubectl get pods",
194
289
  "frequency": 42
195
- },
196
- {
197
- "pattern": "kubectl describe <resource> <name>",
198
- "example": "kubectl describe pod api-7f9b",
199
- "frequency": 17
200
290
  }
201
291
  ],
202
- "last_updated": 1709600000
292
+ "last_updated": 1709600000,
293
+ "processed_commands": [
294
+ "kubectl get pods",
295
+ "kubectl get services",
296
+ "kubectl describe pod api-7f9b"
297
+ ]
298
+ }
299
+ ```
300
+
301
+ ### Group File (`groups/repos/<repo>.json` or `groups/_global.json`)
302
+
303
+ ```json
304
+ {
305
+ "saved": [
306
+ { "cmd": "echo hello", "comment": "test" }
307
+ ],
308
+ "groups": {
309
+ "api": {
310
+ "description": "API troubleshooting",
311
+ "commands": [
312
+ {
313
+ "cmd": "curl -H 'Authorization: Bearer $API_TOKEN' https://api.example.com/users",
314
+ "comment": "list users",
315
+ "vars": [
316
+ { "name": "API_TOKEN", "default": null }
317
+ ]
318
+ }
319
+ ]
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ ### Variable Store (`vars.json`)
326
+
327
+ ```json
328
+ {
329
+ "vars": {
330
+ "API_TOKEN": { "value": "sk-abc123...", "last_used": 1709600000 },
331
+ "DB_HOST": { "value": "staging.db.internal", "last_used": 1709500000 }
332
+ }
203
333
  }
204
334
  ```
205
335
 
@@ -215,5 +345,5 @@ See `docs/decisions/` for detailed ADRs:
215
345
 
216
346
  - [001: JSONL Over SQLite](docs/decisions/001-jsonl-over-sqlite.md)
217
347
  - [002: Apple FM SDK for Patterns](docs/decisions/002-apple-fm-sdk-for-patterns.md)
218
- - [003: No Background Daemon](docs/decisions/003-no-daemon.md)
348
+ - [003: No Daemon, Background Subprocess Instead](docs/decisions/003-no-daemon.md)
219
349
  - [004: Per-Repository JSONL Files](docs/decisions/004-per-repo-jsonl.md)
@@ -76,10 +76,13 @@ passively and speaks only when asked.
76
76
 
77
77
  - Shell hooks append one line per command execution — no
78
78
  prompts, no confirmations, no output.
79
- - Pattern extraction and session grouping run only when the user
80
- explicitly invokes `mem sync`.
81
- - No background daemons, no cron jobs, no scheduled processes.
82
- - The user is always in control of when analysis happens.
79
+ - Pattern extraction runs automatically in a detached background
80
+ process every 20 captures. Completely invisible — no stdout,
81
+ no stderr, no wait. The user never notices it.
82
+ - No daemons, no cron jobs, no persistent processes. The
83
+ background subprocess runs, finishes, and exits on its own.
84
+ - When the user searches or lists, results reflect the latest
85
+ patterns without any manual step.
83
86
 
84
87
  **Why**: A tool that nags or slows the shell will be uninstalled
85
88
  within a day. Silence is a feature.
cli_mem-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,410 @@
1
+ Metadata-Version: 2.4
2
+ Name: cli-mem
3
+ Version: 0.4.0
4
+ Summary: Privacy-first CLI that turns shell history into searchable memory
5
+ Project-URL: GitHub, https://github.com/matinsaurralde/mem
6
+ Author: Matias Insaurralde
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: System :: Shells
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: click
20
+ Requires-Dist: pydantic
21
+ Requires-Dist: rich
22
+ Provides-Extra: ai
23
+ Requires-Dist: apple-fm-sdk; extra == 'ai'
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio; extra == 'dev'
27
+ Requires-Dist: ruff; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ <p align="center">
31
+ <h1 align="center">mem</h1>
32
+ <p align="center">
33
+ <strong>Your shell, remembered.</strong>
34
+ </p>
35
+ <p align="center">
36
+ A privacy-first CLI that captures, searches, and organizes your terminal history<br>
37
+ with on-device AI. Nothing ever leaves your machine.
38
+ </p>
39
+ <p align="center">
40
+ <a href="#install"><img alt="macOS 26+" src="https://img.shields.io/badge/macOS-26%2B-blue?logo=apple&logoColor=white"></a>
41
+ <a href="#install"><img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white"></a>
42
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green"></a>
43
+ <a href="PHILOSOPHY.md"><img alt="Privacy: on-device" src="https://img.shields.io/badge/privacy-100%25%20on--device-brightgreen"></a>
44
+ </p>
45
+ </p>
46
+
47
+ ---
48
+
49
+ ## What mem does
50
+
51
+ mem silently captures every command you type, then lets you search, save, and replay them — scoped to the git repo you're in.
52
+
53
+ ```bash
54
+ mem deploy # search your history
55
+ mem save "cmd" -g ops # save a command to a group
56
+ mem run ops # run the group interactively
57
+ mem vars set API_KEY # store a secret for saved commands
58
+ ```
59
+
60
+ Unlike `Ctrl+R`, mem ranks results by frequency, recency, and the repo you're currently in. Unlike cloud-based tools, everything stays on your machine as plain text files in `~/.mem/`.
61
+
62
+ ---
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ # Homebrew (recommended)
68
+ brew install matinsaurralde/tap/mem
69
+
70
+ # pip
71
+ pip install cli-mem
72
+
73
+ # With AI features (pattern extraction + credential detection)
74
+ pip install "cli-mem[ai]"
75
+ ```
76
+
77
+ Then activate the shell hook for your shell:
78
+
79
+ ```bash
80
+ # zsh
81
+ echo 'eval "$(mem init zsh)"' >> ~/.zshrc
82
+ source ~/.zshrc
83
+
84
+ # bash
85
+ echo 'eval "$(mem init bash)"' >> ~/.bashrc
86
+ source ~/.bashrc
87
+
88
+ # fish
89
+ echo 'mem init fish | source' >> ~/.config/fish/config.fish
90
+ source ~/.config/fish/config.fish
91
+ ```
92
+
93
+ That's it. Every command you type is now silently captured with full context (directory, git repo, exit code, duration).
94
+
95
+ ---
96
+
97
+ ## Search
98
+
99
+ Just type `mem` followed by any keyword. Results are ranked by your current repo.
100
+
101
+ ```bash
102
+ mem kubectl # search by keyword
103
+ mem "docker compose" # search by phrase
104
+ mem deploy -n 20 # more results
105
+ mem deploy --json # machine-readable output
106
+ ```
107
+
108
+ ```
109
+ 1 kubectl apply -f deployment.yaml infra 2h ago
110
+ 2 docker compose up -d backend 1d ago
111
+ 3 fly deploy api 3d ago
112
+ ```
113
+
114
+ ### Patterns
115
+
116
+ mem automatically learns structural patterns from your history using on-device AI. No manual step needed — extraction runs in the background every 20 commands.
117
+
118
+ ```bash
119
+ mem kubectl -p
120
+ ```
121
+
122
+ ```
123
+ Patterns for "kubectl":
124
+
125
+ kubectl get <resource>
126
+ kubectl describe <resource> <name>
127
+ kubectl logs <pod> [--tail=<n>]
128
+ kubectl apply -f <file>
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Groups
134
+
135
+ Groups are named collections of commands — like runbooks you can execute.
136
+
137
+ ### Save commands to a group
138
+
139
+ ```bash
140
+ mem save "kubectl get pods -n production" --group k8s --comment "list pods"
141
+ mem save "docker compose up -d" -g deploy -c "start services"
142
+ ```
143
+
144
+ Save the last command you ran:
145
+
146
+ ```bash
147
+ mem save "!" -g troubleshooting
148
+ ```
149
+
150
+ ### List groups
151
+
152
+ ```bash
153
+ mem list # show all groups and saved commands
154
+ mem list k8s # show commands in a specific group
155
+ mem list -g # global scope only
156
+ mem list -r # current repo only
157
+ mem list --json # JSON output
158
+ ```
159
+
160
+ ### Run a group
161
+
162
+ ```bash
163
+ mem run k8s # run interactively (pick one or all)
164
+ mem run deploy -y # run all without prompts
165
+ ```
166
+
167
+ ### Manage groups
168
+
169
+ ```bash
170
+ mem group rename old new # rename a group
171
+ mem group remove k8s # delete a group
172
+ mem group copy k8s --global # copy from repo to global scope
173
+ mem group edit k8s # open in $EDITOR
174
+ ```
175
+
176
+ ### Export and import
177
+
178
+ ```bash
179
+ mem export k8s # copy JSON to clipboard
180
+ mem export k8s --format markdown # copy as markdown
181
+ mem export k8s --stdout # print instead of clipboard
182
+
183
+ mem import # import from clipboard (auto-detect format + group name)
184
+ mem import -g renamed # import from clipboard with custom group name
185
+ mem import runbook.json -g ops # import from file (auto-detects format)
186
+ mem import runbook.md -g ops # markdown works too
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Variables
192
+
193
+ Saved commands can contain `$VAR_NAME` placeholders that get resolved at runtime. Values never get stored in group files.
194
+
195
+ ### Save commands with variables
196
+
197
+ ```bash
198
+ # Variables are detected automatically from $VAR_NAME tokens
199
+ mem save "ssh -i ~/.ssh/\$KEY_NAME ubuntu@\$BASTION_HOST" -g ssh
200
+
201
+ # Set a default value with --var
202
+ mem save "kubectl get pods -n \$NAMESPACE" -g k8s --var NAMESPACE=production
203
+
204
+ # AI detects hardcoded credentials and suggests variables
205
+ mem save "curl -H 'Authorization: Bearer eyJhbGci...' https://api.example.com/users" -g api
206
+ # Detected possible credential: Bearer token
207
+ # Suggested: curl -H 'Authorization: Bearer $API_TOKEN' ...
208
+ # Variable name [API_TOKEN]: █
209
+ ```
210
+
211
+ ### Resolution priority
212
+
213
+ When `mem run` encounters variables, it resolves them in this order:
214
+
215
+ 1. **Inline arguments** — `mem run api API_TOKEN=abc123`
216
+ 2. **Shell environment** — `export API_TOKEN=abc123`
217
+ 3. **Persistent store** — `mem vars set API_TOKEN`
218
+ 4. **Default value** — from `--var NAME=default` at save time
219
+ 5. **Interactive prompt** — asks you, only as a last resort
220
+
221
+ All prompts are collected upfront before any command runs. With `--yes`, unresolved variables cause an immediate error listing what's missing.
222
+
223
+ ### Variable store
224
+
225
+ For values that persist across sessions but shouldn't be in `.zshrc`:
226
+
227
+ ```bash
228
+ mem vars set API_TOKEN # hidden input (like sudo)
229
+ mem vars set DB_HOST staging.db # inline for non-sensitive values
230
+ mem vars list # shows names only, never values
231
+ mem vars remove API_TOKEN
232
+ mem vars clear
233
+ ```
234
+
235
+ ### Variable status in listings
236
+
237
+ `mem list` shows whether each variable is ready:
238
+
239
+ ```
240
+ ● backend / api
241
+ ──────────────────────────────────────────────────────
242
+ 1. curl -H 'Authorization: Bearer $API_TOKEN' .../users/$USER_ID
243
+ ✓ $API_TOKEN resolved from environment
244
+ ⚠ $USER_ID unset — pass inline: mem run api USER_ID=42
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Scoping
250
+
251
+ Every group and saved command lives in either **repo scope** (tied to the current git repo) or **global scope** (available everywhere).
252
+
253
+ - Inside a git repo: defaults to repo scope
254
+ - Outside a git repo: defaults to global scope
255
+ - Use `--global` / `-g` to force global scope
256
+ - A repo group **shadows** a global group with the same name
257
+
258
+ ---
259
+
260
+ ## Sessions
261
+
262
+ mem groups your commands into work sessions (based on 5-minute idle gaps and repo changes) so you can recall exactly what you did.
263
+
264
+ ```bash
265
+ mem session "api outage" # search sessions by keyword
266
+ mem session debug --json # machine-readable output
267
+ ```
268
+
269
+ ```
270
+ ┌ [1] Session: 2026-03-07 14:30 myapp ──────────────────┐
271
+ │ 1 kubectl logs api-7f9b --tail=100 │
272
+ │ 2 kubectl get pods -n production │
273
+ │ 3 kubectl rollout restart deploy api │
274
+ │ 4 curl -s localhost:8080/health │
275
+ └─────────────────────────────────────────────────────────┘
276
+
277
+ Replay a session? [number/n]: _
278
+ ```
279
+
280
+ Replaying a session executes each command with per-command confirmation.
281
+
282
+ ---
283
+
284
+ ## Other commands
285
+
286
+ ```bash
287
+ mem stats # top commands, repos, totals
288
+ mem stats --json # machine-readable stats
289
+ mem forget "API_KEY=sk-..." # permanently delete matching commands
290
+ mem forget "password" --yes # skip confirmation
291
+ mem init zsh # print shell hook code (also: bash, fish)
292
+ ```
293
+
294
+ ---
295
+
296
+ ## How it works
297
+
298
+ ```
299
+ You type a command
300
+ │
301
+ ▼
302
+ Shell hook (preexec/precmd)
303
+ │
304
+ ▼
305
+ mem _capture ← runs in background, <5ms
306
+ │
307
+ ├─→ Append to ~/.mem/repos/<repo>.jsonl
308
+ └─→ Every 20 captures: background pattern extraction
309
+ ```
310
+
311
+ **Search scoring:**
312
+
313
+ ```
314
+ score = (frequency × 0.4) + (recency × 0.4) + (context × 0.2)
315
+ ```
316
+
317
+ - **Frequency** — how often you've run this command
318
+ - **Recency** — exponential decay, 7-day half-life
319
+ - **Context** — 1.0 same repo, 0.5 same directory prefix, 0.0 otherwise
320
+
321
+ **AI features** use [Apple Foundation Models](https://developer.apple.com/machine-learning/api/) running entirely on your Mac's neural engine. No API keys, no cloud, no data leaves the machine. If Apple Intelligence isn't available, everything still works — you just don't get pattern extraction or credential detection.
322
+
323
+ ---
324
+
325
+ ## Storage
326
+
327
+ All data lives in `~/.mem/` as human-readable plain text:
328
+
329
+ ```
330
+ ~/.mem/
331
+ repos/
332
+ myapp.jsonl # commands captured in this git repo
333
+ _global.jsonl # commands outside any repo
334
+ sessions/
335
+ 2026-03-07.jsonl # work sessions by date
336
+ patterns/
337
+ kubectl.json # AI-extracted command patterns
338
+ docker.json
339
+ groups/
340
+ repos/
341
+ myapp.json # repo-scoped groups and saved commands
342
+ _global.json # global groups and saved commands
343
+ vars.json # persistent variable store (0600 permissions)
344
+ ```
345
+
346
+ Inspect anything:
347
+
348
+ ```bash
349
+ cat ~/.mem/repos/myapp.jsonl
350
+ tail -f ~/.mem/repos/myapp.jsonl # watch commands arrive in real-time
351
+ grep "docker" ~/.mem/repos/*.jsonl # search across repos
352
+ ```
353
+
354
+ Data rotation happens automatically in the background:
355
+
356
+ | Data | Retention |
357
+ |------|-----------|
358
+ | Commands | 90 days |
359
+ | Sessions | 30 days |
360
+ | Patterns | Forever |
361
+
362
+ ---
363
+
364
+ ## Privacy
365
+
366
+ - Zero network requests — not even update checks
367
+ - Zero telemetry — no analytics, no crash reports
368
+ - Zero cloud dependencies — fully offline, always
369
+ - On-device AI only — runs on your Mac's neural engine
370
+ - Plain text storage — no proprietary formats, you own your data
371
+
372
+ Read more in [PHILOSOPHY.md](PHILOSOPHY.md).
373
+
374
+ ---
375
+
376
+ ## Requirements
377
+
378
+ | Requirement | Version |
379
+ |-------------|---------|
380
+ | macOS | 26.0+ |
381
+ | Python | 3.10+ |
382
+ | Apple Intelligence | Optional (for patterns + credential detection) |
383
+
384
+ ---
385
+
386
+ ## Uninstall
387
+
388
+ ```bash
389
+ brew uninstall mem # or: pip uninstall cli-mem
390
+ rm -rf ~/.mem # remove all captured data
391
+ ```
392
+
393
+ Remove the shell hook line from your shell config (`~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish`).
394
+
395
+ ---
396
+
397
+ ## Contributing
398
+
399
+ ```bash
400
+ git clone https://github.com/matinsaurralde/mem.git
401
+ cd mem
402
+ pip install -e ".[dev]"
403
+ pytest
404
+ ```
405
+
406
+ Read [PHILOSOPHY.md](PHILOSOPHY.md) first.
407
+
408
+ ## License
409
+
410
+ [MIT](LICENSE)