memocode 0.2.0__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 (43) hide show
  1. memocode-0.2.2/PKG-INFO +180 -0
  2. {memocode-0.2.0 → memocode-0.2.2}/README.md +6 -5
  3. {memocode-0.2.0 → memocode-0.2.2}/control/brain.py +3 -2
  4. memocode-0.2.2/memocode.egg-info/PKG-INFO +180 -0
  5. {memocode-0.2.0 → memocode-0.2.2}/pyproject.toml +3 -1
  6. {memocode-0.2.0 → memocode-0.2.2}/run.py +31 -34
  7. {memocode-0.2.0 → memocode-0.2.2}/safety/safety.py +139 -20
  8. memocode-0.2.0/PKG-INFO +0 -9
  9. memocode-0.2.0/memocode.egg-info/PKG-INFO +0 -9
  10. {memocode-0.2.0 → memocode-0.2.2}/control/__init__.py +0 -0
  11. {memocode-0.2.0 → memocode-0.2.2}/control/audit.py +0 -0
  12. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/__init__.py +0 -0
  13. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/cli.py +0 -0
  14. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/compressor.py +0 -0
  15. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/config.py +0 -0
  16. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/context_manager.py +0 -0
  17. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/mcp_server.py +0 -0
  18. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/memory/__init__.py +0 -0
  19. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/memory/consolidation.py +0 -0
  20. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/memory/core_memory.py +0 -0
  21. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/memory/forgetting.py +0 -0
  22. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/memory/recent_memory.py +0 -0
  23. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/server.py +0 -0
  24. {memocode-0.2.0 → memocode-0.2.2}/control/chatmem/token_counter.py +0 -0
  25. {memocode-0.2.0 → memocode-0.2.2}/control/fmt.py +0 -0
  26. {memocode-0.2.0 → memocode-0.2.2}/control/llm.py +0 -0
  27. {memocode-0.2.0 → memocode-0.2.2}/control/planner.py +0 -0
  28. {memocode-0.2.0 → memocode-0.2.2}/control/project_manager.py +0 -0
  29. {memocode-0.2.0 → memocode-0.2.2}/memocode.egg-info/SOURCES.txt +0 -0
  30. {memocode-0.2.0 → memocode-0.2.2}/memocode.egg-info/dependency_links.txt +0 -0
  31. {memocode-0.2.0 → memocode-0.2.2}/memocode.egg-info/entry_points.txt +0 -0
  32. {memocode-0.2.0 → memocode-0.2.2}/memocode.egg-info/requires.txt +0 -0
  33. {memocode-0.2.0 → memocode-0.2.2}/memocode.egg-info/top_level.txt +0 -0
  34. {memocode-0.2.0 → memocode-0.2.2}/safety/__init__.py +0 -0
  35. {memocode-0.2.0 → memocode-0.2.2}/safety/backup.py +0 -0
  36. {memocode-0.2.0 → memocode-0.2.2}/safety/policy.py +0 -0
  37. {memocode-0.2.0 → memocode-0.2.2}/setup.cfg +0 -0
  38. {memocode-0.2.0 → memocode-0.2.2}/tools/__init__.py +0 -0
  39. {memocode-0.2.0 → memocode-0.2.2}/tools/file.py +0 -0
  40. {memocode-0.2.0 → memocode-0.2.2}/tools/loader.py +0 -0
  41. {memocode-0.2.0 → memocode-0.2.2}/tools/registry.py +0 -0
  42. {memocode-0.2.0 → memocode-0.2.2}/tools/shell.py +0 -0
  43. {memocode-0.2.0 → memocode-0.2.2}/tools/web.py +0 -0
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: memocode
3
+ Version: 0.2.2
4
+ Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
+ Author: AssassinCHN
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: openai>=1.0
9
+ Requires-Dist: rich>=13.0
10
+ Requires-Dist: prompt_toolkit>=3.0
11
+ Requires-Dist: certifi>=2024.0
12
+
13
+ # Mcode
14
+
15
+ Personal AI coding agent CLI with memory, tool execution, and safety controls.
16
+
17
+ **Author:** AssassinCHN
18
+
19
+ ## Features
20
+
21
+ - **Agentic tool-use loop** — Native function calling (OpenAI / Anthropic compatible), stuck detection, auto mode for fully autonomous task execution
22
+ - **Memory system** — Persistent conversation memory with consolidation, forgetting curve, and cross-session injection
23
+ - **Project management** — Multiple projects with separate memory DBs and working directories
24
+ - **Built-in tools** — Shell execution, file read/write/edit, glob, grep, web fetch
25
+ - **Safety layer** — Human mode (zone checks + confirmation prompts) / Auto mode (whitelist + work_dir hard boundaries) / Bypass mode (session-only, explicit confirmation required)
26
+ - **Extensible** — Drop Python files into `~/.mcode/tools/` for custom tools
27
+
28
+ ## Requirements
29
+
30
+ - Python 3.11+
31
+ - API key for a supported model (MiniMax, Kimi, or any OpenAI-compatible endpoint)
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install memocode
37
+ ```
38
+
39
+ Configure your model in `~/.mcode/agent.json` (created on first run):
40
+
41
+ ```json
42
+ {
43
+ "active_model": "minimax",
44
+ "models": {
45
+ "minimax": {
46
+ "provider": "openai",
47
+ "model": "MiniMax-M2.7",
48
+ "base_url": "https://api.minimaxi.com/v1",
49
+ "api_key_env": "MINIMAX_API_KEY",
50
+ "context_window": 1000000,
51
+ "extra_body": {"reasoning_split": true}
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ```bash
58
+ export MINIMAX_API_KEY=your_key_here
59
+ mcode
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ```bash
65
+ mcode # Start (auto-resumes last project)
66
+ mcode --project myapp # Start with a specific project
67
+ mcode --verbose # Show full tracebacks on errors
68
+ ```
69
+
70
+ ## Slash Commands
71
+
72
+ | Command | Description |
73
+ |---------|-------------|
74
+ | `/help` | Show all commands |
75
+ | `/status` | Show current settings |
76
+ | `/quit` / `/exit` | Exit |
77
+ | `/end` | End session (save to memory) |
78
+ | | |
79
+ | `/btw <question>` | Quick one-shot question — no memory, no tools |
80
+ | `/template [example]` | Show auto-mode task prompt template |
81
+ | | |
82
+ | `/auto [on\|off]` | Toggle auto mode (no prompts, hard boundaries) |
83
+ | `/auto whitelist [list\|add\|remove\|reset]` | Manage auto mode command whitelist |
84
+ | `/safety [on\|off]` | Toggle safety bypass (DANGEROUS, session-only) |
85
+ | | |
86
+ | `/project list` | List all projects |
87
+ | `/project workdir [path]` | Set working directory |
88
+ | `/project rename [<old>] <new>` | Rename a project (defaults to current) |
89
+ | `/project delete <name>` | Delete a project (glob patterns supported) |
90
+ | | |
91
+ | `/model [name]` | Show or switch LLM model |
92
+ | `/tools` | List all loaded tools |
93
+ | `/history [N]` | Show audit log |
94
+ | `/undo` | Undo the last run — restore code and/or rewind conversation |
95
+ | `/rollback` | Restore a specific backed-up file (file only, for audit) |
96
+ | `/rewind [N]` | Rewind conversation to turn N (conversation only) |
97
+ | `/profile` | Show/edit core memory (user profile) |
98
+ | | |
99
+ | `!<command>` | Run shell command directly (safety-checked) |
100
+ | `@<path>` | Attach file contents to your message |
101
+
102
+ ## Auto Mode
103
+
104
+ Auto mode runs the agent fully autonomously — no confirmation prompts, hard safety boundaries (whitelist-only shell commands, writes restricted to `work_dir`).
105
+
106
+ Enable with `/auto on`, then use `/template` for a recommended task prompt structure:
107
+
108
+ ```
109
+ Task: <one-line goal>
110
+ Context: work_dir, language/framework, entry point
111
+ Acceptance criteria: 1) ... 2) ... 3) ...
112
+ Constraints: do NOT modify <files>
113
+ Verify by running: <test command>
114
+ ```
115
+
116
+ ## Safety Modes
117
+
118
+ | Mode | Behavior |
119
+ |------|----------|
120
+ | Human (default) | Prompts for risky operations; backs up files before destructive ops |
121
+ | Auto (`/auto on`) | No prompts; blocks non-whitelisted commands and writes outside `work_dir` |
122
+ | Bypass (`/safety off`) | Skips all checks; session-only; requires typing `yes` to enable |
123
+
124
+ ## Built-in Tools
125
+
126
+ | Tool | Description |
127
+ |------|-------------|
128
+ | `shell_exec` | Run shell commands (streaming output) |
129
+ | `file_read` | Read file with pagination |
130
+ | `file_write` | Write / append to file |
131
+ | `file_edit` | Targeted string replacement in file |
132
+ | `glob` | Find files by pattern (`**/*.py`) |
133
+ | `grep` | Search file content by regex |
134
+ | `web_fetch` | Fetch a URL, returns readable text (HTML stripped) |
135
+
136
+ ## Custom Tools
137
+
138
+ Drop a Python file in `~/.mcode/tools/`:
139
+
140
+ ```python
141
+ from tools.registry import Tool, ToolSchema
142
+
143
+ def _my_tool(param: str) -> str:
144
+ return f"result: {param}"
145
+
146
+ MY_TOOL = Tool(
147
+ schema=ToolSchema(
148
+ name="my_tool",
149
+ description="Description shown to the model",
150
+ parameters={
151
+ "type": "object",
152
+ "properties": {"param": {"type": "string"}},
153
+ "required": ["param"],
154
+ },
155
+ ),
156
+ fn=_my_tool,
157
+ )
158
+ ```
159
+
160
+ ## Project Structure
161
+
162
+ ```
163
+ mcode/
164
+ ├── run.py # CLI entry point
165
+ ├── control/
166
+ │ ├── brain.py # Agent loop, tool dispatch, safety
167
+ │ ├── llm.py # LLM adapter (OpenAI / Anthropic)
168
+ │ ├── project_manager.py # Project registry
169
+ │ ├── audit.py # Audit log
170
+ │ └── chatmem/ # Memory system
171
+ ├── tools/
172
+ │ ├── file.py # file_read/write/edit, glob, grep
173
+ │ ├── shell.py # shell_exec
174
+ │ ├── web.py # web_fetch
175
+ │ └── registry.py # Tool registry + loader
176
+ └── safety/
177
+ ├── safety.py # Zone checks, auto mode rules
178
+ ├── backup.py # File backup
179
+ └── policy.py # Persistent always-allow policies
180
+ ```
@@ -2,6 +2,8 @@
2
2
 
3
3
  Personal AI coding agent CLI with memory, tool execution, and safety controls.
4
4
 
5
+ **Author:** AssassinCHN
6
+
5
7
  ## Features
6
8
 
7
9
  - **Agentic tool-use loop** — Native function calling (OpenAI / Anthropic compatible), stuck detection, auto mode for fully autonomous task execution
@@ -19,9 +21,7 @@ Personal AI coding agent CLI with memory, tool execution, and safety controls.
19
21
  ## Installation
20
22
 
21
23
  ```bash
22
- git clone <repo>
23
- cd mcode
24
- pip install -e .
24
+ pip install memocode
25
25
  ```
26
26
 
27
27
  Configure your model in `~/.mcode/agent.json` (created on first run):
@@ -79,8 +79,9 @@ mcode --verbose # Show full tracebacks on errors
79
79
  | `/model [name]` | Show or switch LLM model |
80
80
  | `/tools` | List all loaded tools |
81
81
  | `/history [N]` | Show audit log |
82
- | `/rollback` | Restore a backed-up file |
83
- | `/rewind [N]` | Rewind session to turn N |
82
+ | `/undo` | Undo the last run — restore code and/or rewind conversation |
83
+ | `/rollback` | Restore a specific backed-up file (file only, for audit) |
84
+ | `/rewind [N]` | Rewind conversation to turn N (conversation only) |
84
85
  | `/profile` | Show/edit core memory (user profile) |
85
86
  | | |
86
87
  | `!<command>` | Run shell command directly (safety-checked) |
@@ -402,8 +402,9 @@ class Brain:
402
402
  "If the user asks you to write or create a file, call file_write. "
403
403
  "If the user asks you to edit a file, call file_edit. "
404
404
  "If the user asks you to fetch a URL or HTTP endpoint, call web_fetch — never fabricate the response. "
405
- "Before editing a file, always read it first with file_read. "
406
- "For targeted changes use file_edit; for new files use file_write."
405
+ "Before editing a file, always read it first with file_read, then proceed to make the edit without stopping. "
406
+ "For targeted changes use file_edit; for new files use file_write. "
407
+ "If a task requires multiple tool calls, keep calling tools until the task is fully done — do not report completion until all actions have been executed via tools."
407
408
  )
408
409
  user_msg = [
409
410
  {"role": "system", "content": _system},
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: memocode
3
+ Version: 0.2.2
4
+ Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
+ Author: AssassinCHN
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: openai>=1.0
9
+ Requires-Dist: rich>=13.0
10
+ Requires-Dist: prompt_toolkit>=3.0
11
+ Requires-Dist: certifi>=2024.0
12
+
13
+ # Mcode
14
+
15
+ Personal AI coding agent CLI with memory, tool execution, and safety controls.
16
+
17
+ **Author:** AssassinCHN
18
+
19
+ ## Features
20
+
21
+ - **Agentic tool-use loop** — Native function calling (OpenAI / Anthropic compatible), stuck detection, auto mode for fully autonomous task execution
22
+ - **Memory system** — Persistent conversation memory with consolidation, forgetting curve, and cross-session injection
23
+ - **Project management** — Multiple projects with separate memory DBs and working directories
24
+ - **Built-in tools** — Shell execution, file read/write/edit, glob, grep, web fetch
25
+ - **Safety layer** — Human mode (zone checks + confirmation prompts) / Auto mode (whitelist + work_dir hard boundaries) / Bypass mode (session-only, explicit confirmation required)
26
+ - **Extensible** — Drop Python files into `~/.mcode/tools/` for custom tools
27
+
28
+ ## Requirements
29
+
30
+ - Python 3.11+
31
+ - API key for a supported model (MiniMax, Kimi, or any OpenAI-compatible endpoint)
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install memocode
37
+ ```
38
+
39
+ Configure your model in `~/.mcode/agent.json` (created on first run):
40
+
41
+ ```json
42
+ {
43
+ "active_model": "minimax",
44
+ "models": {
45
+ "minimax": {
46
+ "provider": "openai",
47
+ "model": "MiniMax-M2.7",
48
+ "base_url": "https://api.minimaxi.com/v1",
49
+ "api_key_env": "MINIMAX_API_KEY",
50
+ "context_window": 1000000,
51
+ "extra_body": {"reasoning_split": true}
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ```bash
58
+ export MINIMAX_API_KEY=your_key_here
59
+ mcode
60
+ ```
61
+
62
+ ## Usage
63
+
64
+ ```bash
65
+ mcode # Start (auto-resumes last project)
66
+ mcode --project myapp # Start with a specific project
67
+ mcode --verbose # Show full tracebacks on errors
68
+ ```
69
+
70
+ ## Slash Commands
71
+
72
+ | Command | Description |
73
+ |---------|-------------|
74
+ | `/help` | Show all commands |
75
+ | `/status` | Show current settings |
76
+ | `/quit` / `/exit` | Exit |
77
+ | `/end` | End session (save to memory) |
78
+ | | |
79
+ | `/btw <question>` | Quick one-shot question — no memory, no tools |
80
+ | `/template [example]` | Show auto-mode task prompt template |
81
+ | | |
82
+ | `/auto [on\|off]` | Toggle auto mode (no prompts, hard boundaries) |
83
+ | `/auto whitelist [list\|add\|remove\|reset]` | Manage auto mode command whitelist |
84
+ | `/safety [on\|off]` | Toggle safety bypass (DANGEROUS, session-only) |
85
+ | | |
86
+ | `/project list` | List all projects |
87
+ | `/project workdir [path]` | Set working directory |
88
+ | `/project rename [<old>] <new>` | Rename a project (defaults to current) |
89
+ | `/project delete <name>` | Delete a project (glob patterns supported) |
90
+ | | |
91
+ | `/model [name]` | Show or switch LLM model |
92
+ | `/tools` | List all loaded tools |
93
+ | `/history [N]` | Show audit log |
94
+ | `/undo` | Undo the last run — restore code and/or rewind conversation |
95
+ | `/rollback` | Restore a specific backed-up file (file only, for audit) |
96
+ | `/rewind [N]` | Rewind conversation to turn N (conversation only) |
97
+ | `/profile` | Show/edit core memory (user profile) |
98
+ | | |
99
+ | `!<command>` | Run shell command directly (safety-checked) |
100
+ | `@<path>` | Attach file contents to your message |
101
+
102
+ ## Auto Mode
103
+
104
+ Auto mode runs the agent fully autonomously — no confirmation prompts, hard safety boundaries (whitelist-only shell commands, writes restricted to `work_dir`).
105
+
106
+ Enable with `/auto on`, then use `/template` for a recommended task prompt structure:
107
+
108
+ ```
109
+ Task: <one-line goal>
110
+ Context: work_dir, language/framework, entry point
111
+ Acceptance criteria: 1) ... 2) ... 3) ...
112
+ Constraints: do NOT modify <files>
113
+ Verify by running: <test command>
114
+ ```
115
+
116
+ ## Safety Modes
117
+
118
+ | Mode | Behavior |
119
+ |------|----------|
120
+ | Human (default) | Prompts for risky operations; backs up files before destructive ops |
121
+ | Auto (`/auto on`) | No prompts; blocks non-whitelisted commands and writes outside `work_dir` |
122
+ | Bypass (`/safety off`) | Skips all checks; session-only; requires typing `yes` to enable |
123
+
124
+ ## Built-in Tools
125
+
126
+ | Tool | Description |
127
+ |------|-------------|
128
+ | `shell_exec` | Run shell commands (streaming output) |
129
+ | `file_read` | Read file with pagination |
130
+ | `file_write` | Write / append to file |
131
+ | `file_edit` | Targeted string replacement in file |
132
+ | `glob` | Find files by pattern (`**/*.py`) |
133
+ | `grep` | Search file content by regex |
134
+ | `web_fetch` | Fetch a URL, returns readable text (HTML stripped) |
135
+
136
+ ## Custom Tools
137
+
138
+ Drop a Python file in `~/.mcode/tools/`:
139
+
140
+ ```python
141
+ from tools.registry import Tool, ToolSchema
142
+
143
+ def _my_tool(param: str) -> str:
144
+ return f"result: {param}"
145
+
146
+ MY_TOOL = Tool(
147
+ schema=ToolSchema(
148
+ name="my_tool",
149
+ description="Description shown to the model",
150
+ parameters={
151
+ "type": "object",
152
+ "properties": {"param": {"type": "string"}},
153
+ "required": ["param"],
154
+ },
155
+ ),
156
+ fn=_my_tool,
157
+ )
158
+ ```
159
+
160
+ ## Project Structure
161
+
162
+ ```
163
+ mcode/
164
+ ├── run.py # CLI entry point
165
+ ├── control/
166
+ │ ├── brain.py # Agent loop, tool dispatch, safety
167
+ │ ├── llm.py # LLM adapter (OpenAI / Anthropic)
168
+ │ ├── project_manager.py # Project registry
169
+ │ ├── audit.py # Audit log
170
+ │ └── chatmem/ # Memory system
171
+ ├── tools/
172
+ │ ├── file.py # file_read/write/edit, glob, grep
173
+ │ ├── shell.py # shell_exec
174
+ │ ├── web.py # web_fetch
175
+ │ └── registry.py # Tool registry + loader
176
+ └── safety/
177
+ ├── safety.py # Zone checks, auto mode rules
178
+ ├── backup.py # File backup
179
+ └── policy.py # Persistent always-allow policies
180
+ ```
@@ -4,8 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memocode"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Personal AI coding agent with memory, tool execution, and safety controls"
9
+ readme = "README.md"
10
+ authors = [{name = "AssassinCHN"}]
9
11
  requires-python = ">=3.11"
10
12
  dependencies = [
11
13
  "openai>=1.0",
@@ -26,7 +26,7 @@ Slash commands:
26
26
  /end End session (save to memory, keep verbatim tail)
27
27
  /history [N] Show last N audit log entries (default 15)
28
28
  /undo Undo a run: restore code and/or rewind conversation
29
- /rollback Restore a backed-up file (file only)
29
+ /rollback Restore a backed-up file (file only, for audit)
30
30
  /rewind [N] Rewind conversation to turn N (conversation only)
31
31
 
32
32
  /project list List all projects
@@ -232,31 +232,6 @@ def cmd_undo(brain: Brain):
232
232
  print("Undo complete.")
233
233
 
234
234
 
235
- def cmd_rollback(brain: Brain):
236
- runs = brain.audit.runs_with_backups(project=brain.projects.current_project)
237
- if not runs:
238
- print("No backups available.")
239
- return
240
-
241
- import time
242
- print("\nRuns with available backups:")
243
- for i, run in enumerate(runs[:10]):
244
- ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(run["ts"]))
245
- status = "✓" if run["success"] else "✗"
246
- print(f" [{i}] {ts} {status} {run['goal'][:60]}")
247
- for orig, bp in run["available_backups"].items():
248
- print(f" {orig} ← {bp}")
249
-
250
- choice = _ask("\nEnter number to restore (or Enter to cancel): ")
251
- if not choice.isdigit() or int(choice) >= len(runs[:10]):
252
- print("Cancelled.")
253
- return
254
-
255
- from safety.backup import restore_backup
256
- for orig, bp in runs[int(choice)]["available_backups"].items():
257
- print(f"Restored: {orig}" if restore_backup(bp) else f"Failed: {bp}")
258
-
259
-
260
235
  def cmd_rewind(brain: Brain, arg: str):
261
236
  turns = brain.memory.list_turns()
262
237
  if not turns:
@@ -264,14 +239,11 @@ def cmd_rewind(brain: Brain, arg: str):
264
239
  return
265
240
 
266
241
  if not arg:
267
- # List turns for user to pick
268
242
  print("\nCurrent session turns:")
269
243
  for t in turns:
270
- user_preview = t["user"].replace("\n", " ")
271
- asst_preview = t["assistant"].replace("\n", " ")
272
- print(f" [{t['index']}] You: {user_preview[:60]}")
273
- if asst_preview:
274
- print(f" Agent: {asst_preview[:60]}")
244
+ print(f" [{t['index']}] You: {t['user'].replace(chr(10), ' ')[:60]}")
245
+ if t["assistant"]:
246
+ print(f" Agent: {t['assistant'].replace(chr(10), ' ')[:60]}")
275
247
  choice = _ask("\nRewind to turn number (or Enter to cancel): ")
276
248
  if not choice.isdigit():
277
249
  print("Cancelled.")
@@ -285,12 +257,37 @@ def cmd_rewind(brain: Brain, arg: str):
285
257
  idx = int(arg)
286
258
  try:
287
259
  brain.memory.rewind_to(idx)
288
- turns = brain.memory.list_turns()
289
- print(f"Rewound to turn [{idx}]. Session now has {len(turns)} turn(s).")
260
+ print(f"Rewound to turn [{idx}]. Session now has {len(brain.memory.list_turns())} turn(s).")
290
261
  except IndexError as e:
291
262
  print(f"Error: {e}")
292
263
 
293
264
 
265
+ def cmd_rollback(brain: Brain):
266
+ runs = brain.audit.runs_with_backups(project=brain.projects.current_project)
267
+ if not runs:
268
+ print("No backups available.")
269
+ return
270
+
271
+ import time
272
+ print("\nRuns with available backups:")
273
+ for i, run in enumerate(runs[:10]):
274
+ ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(run["ts"]))
275
+ status = "✓" if run["success"] else "✗"
276
+ print(f" [{i}] {ts} {status} {run['goal'][:60]}")
277
+ for orig, bp in run["available_backups"].items():
278
+ print(f" {orig} ← {bp}")
279
+
280
+ choice = _ask("\nEnter number to restore (or Enter to cancel): ")
281
+ if not choice.isdigit() or int(choice) >= len(runs[:10]):
282
+ print("Cancelled.")
283
+ return
284
+
285
+ from safety.backup import restore_backup
286
+ for orig, bp in runs[int(choice)]["available_backups"].items():
287
+ print(f"Restored: {orig}" if restore_backup(bp) else f"Failed: {bp}")
288
+
289
+
290
+
294
291
  def cmd_history(brain: Brain, n: int = 15):
295
292
  import time
296
293
  project = brain.projects.current_project
@@ -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
memocode-0.2.0/PKG-INFO DELETED
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: memocode
3
- Version: 0.2.0
4
- Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
- Requires-Python: >=3.11
6
- Requires-Dist: openai>=1.0
7
- Requires-Dist: rich>=13.0
8
- Requires-Dist: prompt_toolkit>=3.0
9
- Requires-Dist: certifi>=2024.0
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: memocode
3
- Version: 0.2.0
4
- Summary: Personal AI coding agent with memory, tool execution, and safety controls
5
- Requires-Python: >=3.11
6
- Requires-Dist: openai>=1.0
7
- Requires-Dist: rich>=13.0
8
- Requires-Dist: prompt_toolkit>=3.0
9
- Requires-Dist: certifi>=2024.0
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