mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat: rebrand OpenClaw references to Hermes during migration
- Add rebrand_text() that replaces OpenClaw, Open Claw, Open-Claw, ClawdBot, and MoltBot with Hermes (case-insensitive, word-boundary) - Apply rebranding to memory entries (MEMORY.md, USER.md, daily memory) - Apply rebranding to SOUL.md and workspace instructions via new transform parameter on copy_file() - Fix moldbot -> moltbot typo across codebase (claw.py, migration script, docs, tests) - Add unit tests for rebrand_text and integration tests for memory and soul migration rebranding
This commit is contained in:
parent
eb2a49f95a
commit
1871227198
7 changed files with 137 additions and 15 deletions
|
|
@ -376,6 +376,24 @@ def backup_existing(path: Path, backup_root: Path) -> Optional[Path]:
|
|||
return dest
|
||||
|
||||
|
||||
# ── Brand rewriting ─────────────────────────────────────────
|
||||
# Replace OpenClaw brand names with Hermes in migrated text so that
|
||||
# memory entries, user profiles, SOUL.md, and workspace instructions
|
||||
# read as self-referential to the new agent identity.
|
||||
_REBRAND_PATTERNS: List[Tuple[re.Pattern, str]] = [
|
||||
(re.compile(r'\bOpen[\s-]?Claw\b', re.IGNORECASE), 'Hermes'),
|
||||
(re.compile(r'\bClawdBot\b', re.IGNORECASE), 'Hermes'),
|
||||
(re.compile(r'\bMoltBot\b', re.IGNORECASE), 'Hermes'),
|
||||
]
|
||||
|
||||
|
||||
def rebrand_text(text: str) -> str:
|
||||
"""Replace OpenClaw / ClawdBot / MoltBot brand names with Hermes."""
|
||||
for pattern, replacement in _REBRAND_PATTERNS:
|
||||
text = pattern.sub(replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
def parse_existing_memory_entries(path: Path) -> List[str]:
|
||||
if not path.exists():
|
||||
return []
|
||||
|
|
@ -782,12 +800,13 @@ class Migrator:
|
|||
path.write_text("\n".join(entries) + "\n", encoding="utf-8")
|
||||
return path
|
||||
|
||||
def copy_file(self, source: Path, destination: Path, kind: str) -> None:
|
||||
def copy_file(self, source: Path, destination: Path, kind: str,
|
||||
transform: Optional[Any] = None) -> None:
|
||||
if not source or not source.exists():
|
||||
return
|
||||
|
||||
if destination.exists():
|
||||
if sha256_file(source) == sha256_file(destination):
|
||||
if not transform and sha256_file(source) == sha256_file(destination):
|
||||
self.record(kind, source, destination, "skipped", "Target already matches source")
|
||||
return
|
||||
if not self.overwrite:
|
||||
|
|
@ -797,7 +816,13 @@ class Migrator:
|
|||
if self.execute:
|
||||
backup_path = self.maybe_backup(destination)
|
||||
ensure_parent(destination)
|
||||
shutil.copy2(source, destination)
|
||||
if transform:
|
||||
content = read_text(source)
|
||||
content = transform(content)
|
||||
destination.write_text(content, encoding="utf-8")
|
||||
shutil.copystat(source, destination)
|
||||
else:
|
||||
shutil.copy2(source, destination)
|
||||
self.record(kind, source, destination, "migrated", backup=str(backup_path) if backup_path else None)
|
||||
else:
|
||||
self.record(kind, source, destination, "migrated", "Would copy")
|
||||
|
|
@ -807,7 +832,7 @@ class Migrator:
|
|||
if not source:
|
||||
self.record("soul", None, self.target_root / "SOUL.md", "skipped", "No OpenClaw SOUL.md found")
|
||||
return
|
||||
self.copy_file(source, self.target_root / "SOUL.md", kind="soul")
|
||||
self.copy_file(source, self.target_root / "SOUL.md", kind="soul", transform=rebrand_text)
|
||||
|
||||
def migrate_workspace_agents(self) -> None:
|
||||
source = self.source_candidate(
|
||||
|
|
@ -821,7 +846,7 @@ class Migrator:
|
|||
self.record("workspace-agents", source, None, "skipped", "No workspace target was provided")
|
||||
return
|
||||
destination = self.workspace_target / WORKSPACE_INSTRUCTIONS_FILENAME
|
||||
self.copy_file(source, destination, kind="workspace-agents")
|
||||
self.copy_file(source, destination, kind="workspace-agents", transform=rebrand_text)
|
||||
|
||||
def migrate_memory(self, source: Optional[Path], destination: Path, limit: int, kind: str) -> None:
|
||||
if not source or not source.exists():
|
||||
|
|
@ -832,6 +857,7 @@ class Migrator:
|
|||
if not incoming:
|
||||
self.record(kind, source, destination, "skipped", "No importable entries found")
|
||||
return
|
||||
incoming = [rebrand_text(entry) for entry in incoming]
|
||||
|
||||
existing = parse_existing_memory_entries(destination)
|
||||
merged, stats, overflowed = merge_entries(existing, incoming, limit)
|
||||
|
|
@ -927,7 +953,7 @@ class Migrator:
|
|||
|
||||
def load_openclaw_config(self) -> Dict[str, Any]:
|
||||
# Check current name and legacy config filenames
|
||||
for name in ("openclaw.json", "clawdbot.json", "moldbot.json"):
|
||||
for name in ("openclaw.json", "clawdbot.json", "moltbot.json"):
|
||||
config_path = self.source_root / name
|
||||
if config_path.exists():
|
||||
try:
|
||||
|
|
@ -1543,6 +1569,7 @@ class Migrator:
|
|||
if not all_incoming:
|
||||
self.record("daily-memory", source_dir, destination, "skipped", "No importable entries found in daily memory files")
|
||||
return
|
||||
all_incoming = [rebrand_text(entry) for entry in all_incoming]
|
||||
|
||||
existing = parse_existing_memory_entries(destination)
|
||||
merged, stats, overflowed = merge_entries(existing, all_incoming, self.memory_limit)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue