[BOUNTY $100] Memanto + Developer Skills: Global Memory Wrapper (#508)#596
[BOUNTY $100] Memanto + Developer Skills: Global Memory Wrapper (#508)#596mkcash wants to merge 2 commits into
Conversation
📝 WalkthroughWalkthroughThis PR introduces ChangesSkills-Memanto Integration Example
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/skills-memanto/memanto_skill_wrapper.py`:
- Around line 99-106: The current construction of the memory dict (variable
"memory") stores raw "output", "query" and "workdir" which can capture sensitive
tokens, credentials or local paths; update the code to sanitize/redact before
persisting: add/use a sanitize function (e.g., sanitize_text or
redact_sensitive) to mask secrets via regex (API keys, tokens, filepaths), avoid
storing os.getcwd() directly (store None, basename only, or a hashed/opt-in
value), truncate and/or hash raw "output" and "query" instead of saving full
text, and add a configuration flag (e.g., allow_unredacted_memory=False) so
callers must opt-in to storing unredacted data; apply the same changes to the
other occurrence around lines 132-143 where "memory" is built.
- Around line 55-57: SkillMemoryManager._load_local_history() should guard
against malformed or unreadable HISTORY_FILE; wrap the
json.loads(HISTORY_FILE.read_text()) call in a try/except that catches
JSONDecodeError and OSError (or a broad Exception if you prefer) and return []
on failure so initialization doesn't crash, optionally logging the error if a
logger (e.g., logging.getLogger(__name__)) is available; keep the existing
behavior of returning [] when HISTORY_FILE does not exist.
- Around line 265-268: The current parsing of args.execute using
args.execute.split() breaks quoted arguments and will IndexError on empty input;
replace it with shell-style tokenization using shlex.split(args.execute) and
gracefully handle empty or malformed input: import shlex, call parts =
shlex.split(args.execute) inside the existing if args.execute: block (catching
ValueError from shlex.split for malformed quoting), then check if parts is
non-empty before assigning skill_name = parts[0] and skill_args = parts[1:],
otherwise log/raise a clear error or skip execution; refer to the args.execute,
parts, skill_name and skill_args variables to locate the code to change.
In `@examples/skills-memanto/README.md`:
- Around line 31-35: Update the fenced diagram block that begins with "Skill
Execution → Memanto captures context → Extracts key decisions → Stores in Brain"
to include a language identifier (e.g., add ```text) on the opening fence so the
block is ```text ... ```, which satisfies markdownlint MD040 and keeps the
README markdown lint-clean.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fa506180-05de-47d6-8ed4-4b7ee7c3ff8c
📒 Files selected for processing (2)
examples/skills-memanto/README.mdexamples/skills-memanto/memanto_skill_wrapper.py
| if HISTORY_FILE.exists(): | ||
| return json.loads(HISTORY_FILE.read_text()) | ||
| return [] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Print targeted lines around the reported range
sed -n '1,120p' examples/skills-memanto/memanto_skill_wrapper.py | nl -ba | sed -n '40,90p'
# Locate _load_local_history and HISTORY_FILE definition/usages
rg -n "_load_local_history|HISTORY_FILE|skill_history\.json" examples/skills-memanto/memanto_skill_wrapper.pyRepository: moorcheh-ai/memanto
Length of output: 106
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="examples/skills-memanto/memanto_skill_wrapper.py"
# Show lines around the reported range with line numbers (no nl dependency)
sed -n '40,90p' "$FILE" | cat -n
# Locate function and filename constant/usages
rg -n "_load_local_history|HISTORY_FILE|skill_history\.json" "$FILE"Repository: moorcheh-ai/memanto
Length of output: 2817
Handle corrupted local history files gracefully.
SkillMemoryManager._load_local_history() directly json.loads(HISTORY_FILE.read_text()); a malformed/unreadable ~/.memanto/skill_history.json will raise (e.g., JSONDecodeError / OSError) and crash initialization instead of falling back to [].
Suggested fix
def _load_local_history(self) -> list[dict]:
"""Load local fallback history."""
if HISTORY_FILE.exists():
- return json.loads(HISTORY_FILE.read_text())
+ try:
+ data = json.loads(HISTORY_FILE.read_text())
+ return data if isinstance(data, list) else []
+ except (json.JSONDecodeError, OSError):
+ return []
return []📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if HISTORY_FILE.exists(): | |
| return json.loads(HISTORY_FILE.read_text()) | |
| return [] | |
| if HISTORY_FILE.exists(): | |
| try: | |
| data = json.loads(HISTORY_FILE.read_text()) | |
| return data if isinstance(data, list) else [] | |
| except (json.JSONDecodeError, OSError): | |
| return [] | |
| return [] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/skills-memanto/memanto_skill_wrapper.py` around lines 55 - 57,
SkillMemoryManager._load_local_history() should guard against malformed or
unreadable HISTORY_FILE; wrap the json.loads(HISTORY_FILE.read_text()) call in a
try/except that catches JSONDecodeError and OSError (or a broad Exception if you
prefer) and return [] on failure so initialization doesn't crash, optionally
logging the error if a logger (e.g., logging.getLogger(__name__)) is available;
keep the existing behavior of returning [] when HISTORY_FILE does not exist.
| memory = { | ||
| "timestamp": datetime.now().isoformat(), | ||
| "skill": skill, | ||
| "query": query[:500], | ||
| "output": output[:1000], | ||
| "metadata": metadata or {}, | ||
| "workdir": os.getcwd(), | ||
| } |
There was a problem hiding this comment.
Avoid storing raw command output and working directory without redaction controls.
This auto-captures potentially sensitive data (tokens, credentials, local usernames/paths) into local disk and remote memory by default.
Suggested direction
+import re
+
+def _redact_sensitive(self, text: str) -> str:
+ patterns = [
+ r'(?i)(api[_-]?key|token|password|secret)\s*[:=]\s*\S+',
+ r'-----BEGIN [A-Z ]+PRIVATE KEY-----[\s\S]+?-----END [A-Z ]+PRIVATE KEY-----',
+ ]
+ redacted = text
+ for p in patterns:
+ redacted = re.sub(p, r'\1=[REDACTED]', redacted)
+ return redacted
+
# in store_skill_execution(...)
+safe_query = self._redact_sensitive(query)
+safe_output = self._redact_sensitive(output)
memory = {
"timestamp": datetime.now().isoformat(),
"skill": skill,
- "query": query[:500],
- "output": output[:1000],
+ "query": safe_query[:500],
+ "output": safe_output[:1000],
"metadata": metadata or {},
- "workdir": os.getcwd(),
+ "workdir": os.getcwd() if os.environ.get("MEMANTO_INCLUDE_WORKDIR") == "1" else None,
}Also applies to: 132-143
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/skills-memanto/memanto_skill_wrapper.py` around lines 99 - 106, The
current construction of the memory dict (variable "memory") stores raw "output",
"query" and "workdir" which can capture sensitive tokens, credentials or local
paths; update the code to sanitize/redact before persisting: add/use a sanitize
function (e.g., sanitize_text or redact_sensitive) to mask secrets via regex
(API keys, tokens, filepaths), avoid storing os.getcwd() directly (store None,
basename only, or a hashed/opt-in value), truncate and/or hash raw "output" and
"query" instead of saving full text, and add a configuration flag (e.g.,
allow_unredacted_memory=False) so callers must opt-in to storing unredacted
data; apply the same changes to the other occurrence around lines 132-143 where
"memory" is built.
| if args.execute: | ||
| parts = args.execute.split() | ||
| skill_name = parts[0] | ||
| skill_args = parts[1:] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="examples/skills-memanto/memanto_skill_wrapper.py"
python - <<'PY'
import os
path="examples/skills-memanto/memanto_skill_wrapper.py"
print("exists:", os.path.exists(path))
print("size:", os.path.getsize(path) if os.path.exists(path) else None)
PY
# Print the relevant region with line numbers
nl -ba "$FILE" | sed -n '230,310p'
# Show argparse setup for args.execute (search for "execute" argument)
rg -n "add_argument\\(.*--execute|--execute|args\\.execute" "$FILE" -SRepository: moorcheh-ai/memanto
Length of output: 150
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="examples/skills-memanto/memanto_skill_wrapper.py"
python - <<'PY'
path="examples/skills-memanto/memanto_skill_wrapper.py"
with open(path, "r", encoding="utf-8") as f:
lines=f.readlines()
start=240
end=290
for i in range(start, min(end, len(lines))):
print(f"{i+1}\t{lines[i].rstrip()}")
PY
rg -n "add_argument\\(.*--execute|--execute\\b|args\\.execute" "$FILE" -SRepository: moorcheh-ai/memanto
Length of output: 1774
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="examples/skills-memanto/memanto_skill_wrapper.py"
# Show imports and argparse creation area
python - <<'PY'
path="examples/skills-memanto/memanto_skill_wrapper.py"
with open(path, "r", encoding="utf-8") as f:
lines=f.readlines()
for i in range(1, min(80, len(lines))+1):
print(f"{i}\t{lines[i-1].rstrip()}")
PY
# Show where --execute is defined
python - <<'PY'
path="examples/skills-memanto/memanto_skill_wrapper.py"
with open(path, "r", encoding="utf-8") as f:
lines=f.readlines()
for i in range(180, min(260, len(lines))+1):
if any(s in lines[i-1] for s in ["--execute", 'args = parser.parse_args', "parser ="]):
pass
for i in range(200, min(250, len(lines))+1):
print(f"{i}\t{lines[i-1].rstrip()}")
PYRepository: moorcheh-ai/memanto
Length of output: 4814
Parse --execute with shell-style splitting and handle empty input
examples/skills-memanto/memanto_skill_wrapper.py currently uses args.execute.split(), which breaks quoted arguments and crashes on empty input (parts[0] raises IndexError).
Suggested fix
+import shlex
...
if args.execute:
- parts = args.execute.split()
+ parts = shlex.split(args.execute)
+ if not parts:
+ parser.error("--execute cannot be empty")
skill_name = parts[0]
skill_args = parts[1:]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/skills-memanto/memanto_skill_wrapper.py` around lines 265 - 268, The
current parsing of args.execute using args.execute.split() breaks quoted
arguments and will IndexError on empty input; replace it with shell-style
tokenization using shlex.split(args.execute) and gracefully handle empty or
malformed input: import shlex, call parts = shlex.split(args.execute) inside the
existing if args.execute: block (catching ValueError from shlex.split for
malformed quoting), then check if parts is non-empty before assigning skill_name
= parts[0] and skill_args = parts[1:], otherwise log/raise a clear error or skip
execution; refer to the args.execute, parts, skill_name and skill_args variables
to locate the code to change.
| ``` | ||
| Skill Execution → Memanto captures context → Extracts key decisions → Stores in Brain | ||
| ↑ ↓ | ||
| └─────────────── Future skills retrieve past context ─────────────────────┘ | ||
| ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced diagram block.
This currently triggers markdownlint MD040 and breaks lint-clean docs.
Suggested fix
-```
+```text
Skill Execution → Memanto captures context → Extracts key decisions → Stores in Brain
↑ ↓
└─────────────── Future skills retrieve past context ─────────────────────┘</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 31-31: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/skills-memanto/README.md` around lines 31 - 35, Update the fenced
diagram block that begins with "Skill Execution → Memanto captures context →
Extracts key decisions → Stores in Brain" to include a language identifier
(e.g., add ```text) on the opening fence so the block is ```text ... ```, which
satisfies markdownlint MD040 and keeps the README markdown lint-clean.
Payment InformationPlease send the bounty to one of the following addresses upon PR merge:
Thank you! |
Memanto + Developer Skills Integration
Memanto as a global memory companion across the mattpocock/skills ecosystem.
Problem
Each CLI skill execution is isolated - context from one skill is invisible when running another.
Solution
Memanto acts as a persistent memory layer that:
Files
examples/skills-memanto/memanto_skill_wrapper.py- CLI wrapper with Memanto integrationexamples/skills-memanto/README.md- Documentation and usage guideSummary by CodeRabbit
New Features
Documentation