feat(memory): add git-backed version control for dream memory files
- Add GitStore class wrapping dulwich for memory file versioning - Auto-commit memory changes during Dream consolidation - Add /dream-log and /dream-restore commands for history browsing - Pass tracked_files as constructor param, generate .gitignore dynamically
This commit is contained in:
+81
-14
@@ -96,23 +96,86 @@ async def cmd_dream(ctx: CommandContext) -> OutboundMessage:
|
||||
|
||||
|
||||
async def cmd_dream_log(ctx: CommandContext) -> OutboundMessage:
|
||||
"""Show the Dream consolidation log."""
|
||||
loop = ctx.loop
|
||||
store = loop.consolidator.store
|
||||
log = store.read_dream_log()
|
||||
if not log:
|
||||
# Check if Dream has ever processed anything
|
||||
"""Show what the last Dream changed.
|
||||
|
||||
Default: diff of the latest commit (HEAD~1 vs HEAD).
|
||||
With /dream-log <sha>: diff of that specific commit.
|
||||
"""
|
||||
store = ctx.loop.consolidator.store
|
||||
git = store.git
|
||||
|
||||
if not git.is_initialized():
|
||||
if store.get_last_dream_cursor() == 0:
|
||||
content = "Dream has not run yet."
|
||||
msg = "Dream has not run yet."
|
||||
else:
|
||||
content = "No dream log yet."
|
||||
msg = "Git not initialized for memory files."
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel, chat_id=ctx.msg.chat_id,
|
||||
content=msg, metadata={"render_as": "text"},
|
||||
)
|
||||
|
||||
args = ctx.args.strip()
|
||||
|
||||
if args:
|
||||
# Show diff of a specific commit
|
||||
sha = args.split()[0]
|
||||
result = git.show_commit_diff(sha)
|
||||
if not result:
|
||||
content = f"Commit `{sha}` not found."
|
||||
else:
|
||||
commit, diff = result
|
||||
content = commit.format(diff)
|
||||
else:
|
||||
content = f"## Dream Log\n\n{log}"
|
||||
# Default: show the latest commit's diff
|
||||
result = git.show_commit_diff(git.log(max_entries=1)[0].sha) if git.log(max_entries=1) else None
|
||||
if result:
|
||||
commit, diff = result
|
||||
content = commit.format(diff)
|
||||
else:
|
||||
content = "No commits yet."
|
||||
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel,
|
||||
chat_id=ctx.msg.chat_id,
|
||||
content=content,
|
||||
metadata={"render_as": "text"},
|
||||
channel=ctx.msg.channel, chat_id=ctx.msg.chat_id,
|
||||
content=content, metadata={"render_as": "text"},
|
||||
)
|
||||
|
||||
|
||||
async def cmd_dream_restore(ctx: CommandContext) -> OutboundMessage:
|
||||
"""Restore memory files from a previous dream commit.
|
||||
|
||||
Usage:
|
||||
/dream-restore — list recent commits
|
||||
/dream-restore <sha> — revert a specific commit
|
||||
"""
|
||||
store = ctx.loop.consolidator.store
|
||||
git = store.git
|
||||
if not git.is_initialized():
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel, chat_id=ctx.msg.chat_id,
|
||||
content="Git not initialized for memory files.",
|
||||
)
|
||||
|
||||
args = ctx.args.strip()
|
||||
if not args:
|
||||
# Show recent commits for the user to pick
|
||||
commits = git.log(max_entries=10)
|
||||
if not commits:
|
||||
content = "No commits found."
|
||||
else:
|
||||
lines = ["## Recent Dream Commits\n", "Use `/dream-restore <sha>` to revert a commit.\n"]
|
||||
for c in commits:
|
||||
lines.append(f"- `{c.sha}` {c.message.splitlines()[0]} ({c.timestamp})")
|
||||
content = "\n".join(lines)
|
||||
else:
|
||||
sha = args.split()[0]
|
||||
new_sha = git.revert(sha)
|
||||
if new_sha:
|
||||
content = f"Reverted commit `{sha}` → new commit `{new_sha}`."
|
||||
else:
|
||||
content = f"Failed to revert commit `{sha}`. Check if the SHA is correct."
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel, chat_id=ctx.msg.chat_id,
|
||||
content=content, metadata={"render_as": "text"},
|
||||
)
|
||||
|
||||
|
||||
@@ -135,7 +198,8 @@ def build_help_text() -> str:
|
||||
"/restart — Restart the bot",
|
||||
"/status — Show bot status",
|
||||
"/dream — Manually trigger Dream consolidation",
|
||||
"/dream-log — Show Dream consolidation log",
|
||||
"/dream-log — Show what the last Dream changed",
|
||||
"/dream-restore — Revert memory to a previous state",
|
||||
"/help — Show available commands",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
@@ -150,4 +214,7 @@ def register_builtin_commands(router: CommandRouter) -> None:
|
||||
router.exact("/status", cmd_status)
|
||||
router.exact("/dream", cmd_dream)
|
||||
router.exact("/dream-log", cmd_dream_log)
|
||||
router.prefix("/dream-log ", cmd_dream_log)
|
||||
router.exact("/dream-restore", cmd_dream_restore)
|
||||
router.prefix("/dream-restore ", cmd_dream_restore)
|
||||
router.exact("/help", cmd_help)
|
||||
|
||||
Reference in New Issue
Block a user