memocode 0.2.1__tar.gz → 0.2.2__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.
Files changed (41) hide show
  1. {memocode-0.2.1 → memocode-0.2.2}/PKG-INFO +2 -4
  2. {memocode-0.2.1 → memocode-0.2.2}/README.md +1 -3
  3. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/PKG-INFO +2 -4
  4. {memocode-0.2.1 → memocode-0.2.2}/pyproject.toml +1 -1
  5. {memocode-0.2.1 → memocode-0.2.2}/safety/safety.py +139 -20
  6. {memocode-0.2.1 → memocode-0.2.2}/control/__init__.py +0 -0
  7. {memocode-0.2.1 → memocode-0.2.2}/control/audit.py +0 -0
  8. {memocode-0.2.1 → memocode-0.2.2}/control/brain.py +0 -0
  9. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/__init__.py +0 -0
  10. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/cli.py +0 -0
  11. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/compressor.py +0 -0
  12. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/config.py +0 -0
  13. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/context_manager.py +0 -0
  14. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/mcp_server.py +0 -0
  15. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/memory/__init__.py +0 -0
  16. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/memory/consolidation.py +0 -0
  17. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/memory/core_memory.py +0 -0
  18. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/memory/forgetting.py +0 -0
  19. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/memory/recent_memory.py +0 -0
  20. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/server.py +0 -0
  21. {memocode-0.2.1 → memocode-0.2.2}/control/chatmem/token_counter.py +0 -0
  22. {memocode-0.2.1 → memocode-0.2.2}/control/fmt.py +0 -0
  23. {memocode-0.2.1 → memocode-0.2.2}/control/llm.py +0 -0
  24. {memocode-0.2.1 → memocode-0.2.2}/control/planner.py +0 -0
  25. {memocode-0.2.1 → memocode-0.2.2}/control/project_manager.py +0 -0
  26. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/SOURCES.txt +0 -0
  27. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/dependency_links.txt +0 -0
  28. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/entry_points.txt +0 -0
  29. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/requires.txt +0 -0
  30. {memocode-0.2.1 → memocode-0.2.2}/memocode.egg-info/top_level.txt +0 -0
  31. {memocode-0.2.1 → memocode-0.2.2}/run.py +0 -0
  32. {memocode-0.2.1 → memocode-0.2.2}/safety/__init__.py +0 -0
  33. {memocode-0.2.1 → memocode-0.2.2}/safety/backup.py +0 -0
  34. {memocode-0.2.1 → memocode-0.2.2}/safety/policy.py +0 -0
  35. {memocode-0.2.1 → memocode-0.2.2}/setup.cfg +0 -0
  36. {memocode-0.2.1 → memocode-0.2.2}/tools/__init__.py +0 -0
  37. {memocode-0.2.1 → memocode-0.2.2}/tools/file.py +0 -0
  38. {memocode-0.2.1 → memocode-0.2.2}/tools/loader.py +0 -0
  39. {memocode-0.2.1 → memocode-0.2.2}/tools/registry.py +0 -0
  40. {memocode-0.2.1 → memocode-0.2.2}/tools/shell.py +0 -0
  41. {memocode-0.2.1 → memocode-0.2.2}/tools/web.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memocode
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
5
  Author: AssassinCHN
6
6
  Requires-Python: >=3.11
@@ -33,9 +33,7 @@ Personal AI coding agent CLI with memory, tool execution, and safety controls.
33
33
  ## Installation
34
34
 
35
35
  ```bash
36
- git clone <repo>
37
- cd mcode
38
- pip install -e .
36
+ pip install memocode
39
37
  ```
40
38
 
41
39
  Configure your model in `~/.mcode/agent.json` (created on first run):
@@ -21,9 +21,7 @@ Personal AI coding agent CLI with memory, tool execution, and safety controls.
21
21
  ## Installation
22
22
 
23
23
  ```bash
24
- git clone <repo>
25
- cd mcode
26
- pip install -e .
24
+ pip install memocode
27
25
  ```
28
26
 
29
27
  Configure your model in `~/.mcode/agent.json` (created on first run):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memocode
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
5
  Author: AssassinCHN
6
6
  Requires-Python: >=3.11
@@ -33,9 +33,7 @@ Personal AI coding agent CLI with memory, tool execution, and safety controls.
33
33
  ## Installation
34
34
 
35
35
  ```bash
36
- git clone <repo>
37
- cd mcode
38
- pip install -e .
36
+ pip install memocode
39
37
  ```
40
38
 
41
39
  Configure your model in `~/.mcode/agent.json` (created on first run):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memocode"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Personal AI coding agent with memory, tool execution, and safety controls"
9
9
  readme = "README.md"
10
10
  authors = [{name = "AssassinCHN"}]
@@ -82,7 +82,7 @@ def _extract_paths(command: str, work_dir: str | None = None) -> list[str]:
82
82
  # relative path (./foo, ../foo) — resolve against work_dir or cwd
83
83
  base = work_dir or os.getcwd()
84
84
  ep = os.path.normpath(os.path.join(base, p))
85
- if not ep.startswith(("/proc/", "/sys/")):
85
+ if not ep.startswith(("/proc/", "/sys/")) and ep not in ("/dev/null", "/dev/stdout", "/dev/stderr"):
86
86
  result.append(ep)
87
87
  return result
88
88
 
@@ -266,31 +266,150 @@ def check(task, work_dir: str | None = None,
266
266
  # Command injection patterns
267
267
  _INJECTION_RE = re.compile(r'\$\(|`')
268
268
 
269
- # Split shell command into pipe/chain segments
270
- _SHELL_SEP_RE = re.compile(r'\|\|?|&&?|;')
269
+ # Leading KEY=value env-var assignments (e.g. PYTHONPATH=... FOO=bar cmd ...)
270
+ _ENV_PREFIX_RE = re.compile(r'^(\w+=\S*\s*)+')
271
271
 
272
272
  DEFAULT_AUTO_WHITELIST: list[str] = [
273
- # Filesystem read
274
- "ls", "find", "cat", "head", "tail", "wc", "stat", "file", "du", "df",
275
- "echo", "printenv", "env", "date", "whoami", "id", "uname", "pwd", "which", "type",
276
- # Text processing
277
- "grep", "rg", "awk", "sed", "cut", "sort", "uniq", "diff", "tr", "xargs",
278
- # Git (all subcommands)
273
+ # ── Navigation ───────────────────────────────────────────────────────────
274
+ "cd", "pwd", "pushd", "popd", "dirs",
275
+
276
+ # ── File inspection ──────────────────────────────────────────────────────
277
+ "ls", "ll", "la", "find", "locate", "which", "type", "file",
278
+ "stat", "du", "df", "realpath", "basename", "dirname", "readlink",
279
+ "lsblk", "lscpu", "lshw", "lsof",
280
+
281
+ # ── File read ────────────────────────────────────────────────────────────
282
+ "cat", "head", "tail", "less", "more", "bat",
283
+ "wc", "od", "xxd", "hexdump", "strings",
284
+
285
+ # ── File create / copy / move ─────────────────────────────────────────────
286
+ "mkdir", "touch", "cp", "mv", "ln", "mktemp", "tee", "split", "install",
287
+
288
+ # ── Archive ──────────────────────────────────────────────────────────────
289
+ "tar", "zip", "unzip", "gzip", "gunzip", "bzip2", "bunzip2",
290
+ "xz", "unxz", "zcat", "7z", "7za",
291
+
292
+ # ── Text processing ──────────────────────────────────────────────────────
293
+ "grep", "rg", "ag", "awk", "sed", "cut", "sort", "uniq",
294
+ "diff", "patch", "tr", "xargs",
295
+ "paste", "join", "expand", "unexpand", "nl", "fold", "column",
296
+ "echo", "printf", "iconv",
297
+
298
+ # ── Encoding / hashing ───────────────────────────────────────────────────
299
+ "base64", "md5sum", "sha1sum", "sha256sum", "sha512sum", "shasum", "cksum",
300
+ "openssl",
301
+
302
+ # ── Search ───────────────────────────────────────────────────────────────
303
+ "fd", "fzf",
304
+
305
+ # ── Environment / system info ─────────────────────────────────────────────
306
+ "env", "printenv", "date", "uname", "hostname", "uptime",
307
+ "whoami", "id", "groups",
308
+ "ps", "pgrep", "pidof", "top", "htop", "free", "vmstat", "iostat",
309
+ "ulimit", "time", "bc",
310
+
311
+ # ── Process control ───────────────────────────────────────────────────────
312
+ "sleep", "wait", "timeout", "watch", "nohup",
313
+ "true", "false", "test", "seq", "kill", "pkill",
314
+
315
+ # ── Network (read / fetch) ─────────────────────────────────────────────────
316
+ "curl", "wget", "http",
317
+ "ping", "traceroute", "nslookup", "dig", "host",
318
+ "netstat", "ss", "ifconfig", "ip",
319
+ "ssh", "scp", "rsync",
320
+
321
+ # ── Permissions ──────────────────────────────────────────────────────────
322
+ "chmod", "chown", "chgrp", "umask",
323
+
324
+ # ── Git ──────────────────────────────────────────────────────────────────
279
325
  "git",
280
- # Python / Node
281
- "python", "python3", "node",
282
- # Package info (read-only subcommands)
283
- "pip list", "pip show", "pip freeze",
284
- "pip3 list", "pip3 show", "pip3 freeze",
285
- "npm list", "npm outdated",
286
- # Process / system info
287
- "ps", "top", "free", "lsof", "uptime",
326
+
327
+ # ── Python ───────────────────────────────────────────────────────────────
328
+ "python", "python3", "python3.10", "python3.11", "python3.12", "python3.13",
329
+ "pip", "pip3", "pipenv", "poetry", "uv",
330
+ "pytest", "mypy", "pyright", "ruff", "flake8", "pylint",
331
+ "black", "isort", "autopep8",
332
+ "ipython", "jupyter",
333
+ "pyenv",
334
+
335
+ # ── Node / JS ────────────────────────────────────────────────────────────
336
+ "node", "npm", "npx", "yarn", "pnpm", "bun", "deno",
337
+ "jest", "mocha", "vitest", "eslint", "prettier", "tsc",
338
+ "nvm",
339
+
340
+ # ── Compiled languages ────────────────────────────────────────────────────
341
+ "gcc", "g++", "clang", "clang++", "cc", "c++",
342
+ "make", "cmake", "ninja", "meson", "bear",
343
+ "cargo", "rustc", "rustfmt",
344
+ "go", "gofmt",
345
+ "java", "javac", "jar", "mvn", "gradle",
346
+ "dotnet",
347
+ "swift", "swiftc",
348
+ "ruby", "gem", "bundle", "rspec",
349
+ "perl", "php", "composer",
350
+ "lua", "Rscript",
351
+
352
+ # ── Shell scripting ───────────────────────────────────────────────────────
353
+ "bash", "sh", "zsh", "shellcheck",
354
+ "source",
355
+
356
+ # ── Data / serialisation ──────────────────────────────────────────────────
357
+ "jq", "yq",
358
+
359
+ # ── Containers / orchestration ────────────────────────────────────────────
360
+ "docker", "docker-compose", "docker compose", "podman",
361
+ "kubectl", "helm", "minikube", "kind",
362
+
363
+ # ── Cloud CLIs ────────────────────────────────────────────────────────────
364
+ "aws", "gcloud", "az",
365
+
366
+ # ── Database clients ──────────────────────────────────────────────────────
367
+ "mysql", "mysqldump", "psql", "pg_dump", "sqlite3",
368
+ "redis-cli",
369
+
370
+ # ── Misc ─────────────────────────────────────────────────────────────────
371
+ "man", "tput", "stty", "locale",
372
+ "systemctl", "service",
288
373
  ]
289
374
 
290
375
 
376
+ def _split_shell_ops(command: str) -> list[str]:
377
+ """Split on && || | ; but NOT inside single- or double-quoted strings."""
378
+ segments: list[str] = []
379
+ current: list[str] = []
380
+ in_single = False
381
+ in_double = False
382
+ i = 0
383
+ while i < len(command):
384
+ c = command[i]
385
+ if c == "'" and not in_double:
386
+ in_single = not in_single
387
+ current.append(c)
388
+ elif c == '"' and not in_single:
389
+ in_double = not in_double
390
+ current.append(c)
391
+ elif not in_single and not in_double:
392
+ two = command[i:i + 2]
393
+ if two in ("&&", "||"):
394
+ segments.append("".join(current))
395
+ current = []
396
+ i += 2
397
+ continue
398
+ elif c in ("|", ";"):
399
+ segments.append("".join(current))
400
+ current = []
401
+ else:
402
+ current.append(c)
403
+ else:
404
+ current.append(c)
405
+ i += 1
406
+ segments.append("".join(current))
407
+ return segments
408
+
409
+
291
410
  def _matches_whitelist(segment: str, whitelist: list[str]) -> bool:
292
- """Return True if the command segment starts with a whitelist entry (word boundary)."""
293
- seg = segment.lower().strip()
411
+ """Return True if the command segment (after stripping env-var prefixes) starts with a whitelist entry."""
412
+ seg = _ENV_PREFIX_RE.sub("", segment).lower().strip()
294
413
  for entry in whitelist:
295
414
  e = entry.lower().strip()
296
415
  if seg == e or seg.startswith(e + " "):
@@ -352,7 +471,7 @@ def check_auto(task, work_dir: str | None = None,
352
471
  reason="Auto mode: command injection pattern blocked")
353
472
 
354
473
  # Whitelist check — every pipe/chain segment must be in whitelist
355
- for seg in _SHELL_SEP_RE.split(command):
474
+ for seg in _split_shell_ops(command):
356
475
  seg = seg.strip()
357
476
  if not seg:
358
477
  continue
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes