Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions examples/claudecode-skills-memanto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Claude Code Skills + Memanto Memory Bridge

This example shows how Memanto can act as a global memory companion across separate developer skill executions.

The bridge has two lifecycle hooks:

- `before_skill(...)`: recall relevant engineering memory and return a concise context block that can be appended to a skill prompt.
- `after_skill(...)`: extract durable project decisions, coding preferences, and codebase quirks from a completed skill transcript, then store them in Memanto.
- `run_with_memory(...)`: wrap any existing skill runner callable with both hooks, so teams can drop the bridge around their current `/tdd`, `/handoff`, or custom skill executor without rewriting those skills.

![Cross-skill memory demo](assets/demo.gif)

## Why This Matters

Developer skills are intentionally small and focused. That is useful, but it means a decision captured during a review skill can disappear before a later testing or implementation skill starts.

This example keeps those decisions outside the individual skill run:

1. `/grill-with-docs` reviews an architecture plan and records durable decisions.
2. A fresh `/tdd` run asks for tests in the same project.
3. Memanto recalls the earlier decisions and injects them as a compact engineering-memory block.

## Files

```text
examples/claudecode-skills-memanto/
|-- README.md
|-- assets/demo.gif
|-- make_demo_gif.py
|-- memory_backends.py
|-- requirements.txt
|-- run_cross_skill_demo.py
|-- skill_memory_bridge.py
`-- tests/test_skill_memory_bridge.py
```

## Quick Start

```bash
cd examples/claudecode-skills-memanto
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Runs without external keys by using the local JSON backend.
python run_cross_skill_demo.py --backend file --reset
```

## Run With Memanto

Install and configure Memanto first:

```bash
pip install memanto
memanto
```

Then run the same bridge against the real Memanto CLI backend:

```bash
python run_cross_skill_demo.py --backend memanto --agent-id claudecode-skills-demo
```

## Integration Pattern

Wrap skill execution with the bridge directly:

```python
from skill_memory_bridge import SkillMemoryBridge, SkillRun

bridge = SkillMemoryBridge(memory_backend)
run = SkillRun(
skill_name="/tdd",
task="Add tests for invoice webhook idempotency",
file_paths=["apps/billing/webhooks/stripe.ts"],
)

memory_context = bridge.before_skill(run)

skill_prompt = f"{memory_context}\n\n{original_skill_prompt}"

result = run_skill(skill_prompt)

bridge.after_skill(run, result.transcript)
```

Or use the executor-agnostic wrapper when you already have a function that runs a skill:

```python
def run_skill(prompt: str) -> str:
# Call your existing Claude Code, shell, or local skill runner here.
return "Learning: Invoice export tests should preserve customer locale."

result = bridge.run_with_memory(
run,
"Create a handoff note for the invoice export branch.",
run_skill,
)

print(result.prompt)
print(result.stored_memories)
```

`SkillRun.metadata` can carry project, framework, branch, tenant, or issue identifiers into the recall query without changing the bridge internals:

```python
run = SkillRun(
skill_name="/tdd",
task="Add route tests",
file_paths=["apps/mobile/app/(tabs)/index.tsx"],
metadata={"framework": "expo-router", "project": "mobile-app"},
)
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The bridge deliberately stores only durable engineering facts:

- `Decision: Keep Stripe webhook handlers idempotent by event id.`
- `Preference: Tests should cover replayed webhook payloads.`
- `Quirk: Billing code stores timestamps as UTC ISO strings.`

It avoids saving full prompts, private credentials, or large transient logs.

## Verification

Regenerate the GIF:

```bash
python make_demo_gif.py
```

Run the demo:

```bash
python run_cross_skill_demo.py --backend file --reset
```

Run the focused offline tests:

```bash
python -m unittest discover -s tests -v
```

Expected output includes:

```text
MEMANTO ENGINEERING MEMORY
- Decision: Keep billing writes idempotent by Stripe event id.
- Preference: Add replay tests before changing webhook behavior.
- Quirk: Billing timestamps are stored as UTC ISO strings.
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions examples/claudecode-skills-memanto/make_demo_gif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Generate a compact GIF for the Claude Code skills memory demo."""

from __future__ import annotations

from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

WIDTH = 1040
HEIGHT = 640
BG = (15, 19, 27)
PANEL = (27, 34, 45)
TEXT = (238, 244, 250)
MUTED = (149, 166, 184)
GREEN = (91, 205, 149)
BLUE = (112, 177, 255)


STEPS = [
(
"Skill run 1: /grill-with-docs",
[
"Decision: Keep billing writes idempotent by Stripe event id.",
"Preference: Add replay tests before webhook changes.",
"Quirk: Billing timestamps are UTC ISO strings.",
],
),
(
"after_skill hook",
[
"Extract durable engineering memory.",
"Store only decisions, preferences, quirks, constraints.",
"Skip full prompts, secrets, and noisy logs.",
],
),
(
"Skill run 2: /tdd starts fresh",
[
"Task: add tests for Stripe webhook replay.",
"Files: stripe.ts, stripe.test.ts",
"Graph state is new; Memanto memory persists.",
],
),
(
"before_skill hook",
[
"MEMANTO ENGINEERING MEMORY",
"- Keep billing writes idempotent by Stripe event id.",
"- Add replay tests before changing webhook behavior.",
"- Billing timestamps are stored as UTC ISO strings.",
],
),
(
"Prompt injection",
[
"Append compact memory block to the next skill prompt.",
"The /tdd skill now sees the prior review decisions.",
"No manual context shoving between sessions.",
],
),
]


def main() -> None:
"""Render the animated walkthrough GIF into the local assets directory."""
assets_dir = Path(__file__).parent / "assets"
assets_dir.mkdir(exist_ok=True)
output = assets_dir / "demo.gif"
frames: list[Image.Image] = []
font = _font(30)
small = _font(23)
tiny = _font(20)

for index, (title, lines) in enumerate(STEPS, start=1):
for _ in range(6):
image = Image.new("RGB", (WIDTH, HEIGHT), BG)
draw = ImageDraw.Draw(image)
draw.rounded_rectangle((44, 44, WIDTH - 44, HEIGHT - 44), radius=18, fill=PANEL)
draw.text((82, 82), "Claude Code Skills + Memanto", font=font, fill=GREEN)
draw.text((82, 126), f"Step {index}/5: {title}", font=small, fill=TEXT)
draw.line((82, 174, WIDTH - 82, 174), fill=(63, 77, 94), width=2)

y = 218
for line in lines:
color = BLUE if line.startswith("MEMANTO") else TEXT
draw.text((104, y), line, font=small, fill=color)
y += 50

draw.text(
(82, HEIGHT - 96),
"Global active memory across isolated developer skills",
font=tiny,
fill=MUTED,
)
frames.append(image)

frames[0].save(
output,
save_all=True,
append_images=frames[1:],
duration=1000,
loop=0,
optimize=True,
)
print(output)


def _font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
"""Load a readable system font with a portable default fallback."""
candidates = [
"/System/Library/Fonts/SFNS.ttf",
"/System/Library/Fonts/Supplemental/Arial.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
]
for candidate in candidates:
path = Path(candidate)
if path.exists():
return ImageFont.truetype(str(path), size=size)
return ImageFont.load_default()


if __name__ == "__main__":
main()
Loading