476 lines
21 KiB
Markdown
476 lines
21 KiB
Markdown
# Migration Guide
|
|
|
|
OpenSquilla can import state from OpenClaw and Hermes Agent into OpenSquilla
|
|
native files. The migration commands are designed to be previewed first, then
|
|
applied explicitly.
|
|
|
|
Supported migration paths:
|
|
|
|
- Auto-detect everything found under your home: `opensquilla migrate`
|
|
- OpenClaw -> OpenSquilla: `opensquilla migrate openclaw`
|
|
- Hermes Agent -> OpenSquilla: `opensquilla migrate hermes`
|
|
|
|
`opensquilla migrate` (with no subcommand) scans `~/.openclaw` and
|
|
`~/.hermes` and decides what to do based on what it finds:
|
|
|
|
- **Nothing detected**: prints the default paths it checked and exits 0.
|
|
- **Exactly one detected**: runs that migrator. No prompt, no flag needed.
|
|
- **Both detected, interactive (TTY) shell**: opens a multi-select prompt
|
|
so you can pick one, both, or neither.
|
|
- **Both detected, non-interactive context (CI, piped, `--json`)**: prints
|
|
the detected sources and exits 0 without migrating. Re-run with
|
|
`--source openclaw,hermes` (or a subset) to opt in explicitly.
|
|
|
|
When both sources are selected, OpenSquilla runs OpenClaw first and Hermes
|
|
second. The second migrator sees whatever the first one wrote, so its
|
|
existing per-file dedupe / persona-conflict rules kick in normally. Use
|
|
`--source openclaw` or `--source hermes` (comma-separated) to narrow the
|
|
selection.
|
|
|
|
If you are running from a source checkout instead of an installed
|
|
`opensquilla` command, prefix the examples with `uv run`:
|
|
|
|
```sh
|
|
uv run opensquilla migrate openclaw --json
|
|
uv run opensquilla migrate hermes --json
|
|
```
|
|
|
|
## Before You Start
|
|
|
|
1. Stop any running OpenSquilla gateway if it is using the target home.
|
|
2. Make a manual backup of your OpenSquilla home if you need whole-home
|
|
rollback. The migrators can back up overwritten items, but they do not yet
|
|
create a complete pre-migration snapshot of `~/.opensquilla`.
|
|
3. Run a dry run first and inspect the report.
|
|
4. Do not pass `--migrate-secrets` until you have reviewed what will be copied.
|
|
|
|
Default locations:
|
|
|
|
- OpenSquilla home: `~/.opensquilla`
|
|
- OpenClaw source home: `~/.openclaw`
|
|
- Hermes Agent source home: `~/.hermes`
|
|
|
|
On Windows, these are under your user profile, for example
|
|
`C:\Users\<you>\.opensquilla`.
|
|
|
|
## Common Options
|
|
|
|
Both migration commands support the same main controls:
|
|
|
|
| Option | Meaning |
|
|
| --- | --- |
|
|
| `--source PATH` | Source OpenClaw or Hermes Agent home. |
|
|
| `--config PATH` | OpenSquilla config path to preview or write. |
|
|
| `--apply` | Apply the migration. Without this, the command is a dry run. |
|
|
| `--migrate-secrets` | Copy recognized secrets such as API keys and channel tokens. Defaults to false. |
|
|
| `--overwrite` | Allow replacing existing targets. Existing overwritten items are backed up where supported. |
|
|
| `--preset user-data` | Migrate only user-facing data such as persona, memory, and skills. |
|
|
| `--preset full` | Migrate user data plus supported config/runtime artifacts. This is the default. |
|
|
| `--include IDS` | Include only selected migration option ids. Comma-separated. |
|
|
| `--exclude IDS` | Exclude selected migration option ids. Comma-separated. |
|
|
| `--skill-conflict MODE` | Handle imported skill name conflicts: `skip`, `overwrite`, or `rename`. |
|
|
| `--json` | Print a machine-readable report. Recommended for dry runs. |
|
|
|
|
## OpenClaw -> OpenSquilla
|
|
|
|
Use this path if your existing agent state is in an OpenClaw home.
|
|
|
|
Preview first:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --json
|
|
```
|
|
|
|
Preview a custom OpenClaw home:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --source /path/to/.openclaw --json
|
|
```
|
|
|
|
Apply without secrets:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --apply
|
|
```
|
|
|
|
Apply and copy recognized secrets:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --apply --migrate-secrets
|
|
```
|
|
|
|
Apply and rename imported skill conflicts instead of skipping them:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --apply --skill-conflict rename
|
|
```
|
|
|
|
### What Is Migrated From OpenClaw
|
|
|
|
OpenSquilla currently maps OpenClaw data into OpenSquilla-native locations:
|
|
|
|
- Workspace persona files such as `SOUL.md`, `AGENTS.md`, and `USER.md`.
|
|
- Long-term memory and daily memory where supported.
|
|
- User skills and shared skills, imported under `~/.opensquilla/skills/openclaw-imports/`.
|
|
- TTS assets, while unsupported TTS configuration is archived for review.
|
|
- Command allowlists.
|
|
- Model config, including string, object, and alias/catalog forms.
|
|
- Provider keys from `.env` or provider config when `--migrate-secrets` is set.
|
|
- MCP server definitions where OpenSquilla has native fields.
|
|
- Telegram, Discord, and Slack channel config where OpenSquilla has native channel support.
|
|
- Selected agent and tool settings with OpenSquilla-native equivalents.
|
|
- Unsupported or unsafe OpenClaw artifacts are archived for manual review.
|
|
|
|
The OpenClaw migrator also rewrites OpenClaw branding in migrated user-facing
|
|
workspace text to OpenSquilla branding and archives the original changed text
|
|
for review.
|
|
|
|
**Mixed-subject prose is kept verbatim.** When a workspace file (or a
|
|
single MEMORY.md block) already mentions `OpenSquilla` (any case), the
|
|
migrator skips the mechanical rebrand for that file/block and writes it
|
|
verbatim. Mechanical replacement of `OpenClaw` -> `OpenSquilla` in prose
|
|
that already names both runtimes as distinct entities produces factual
|
|
errors and self-referential nonsense. The report records
|
|
`details.rebrand_skipped: "mentions-opensquilla"` for the affected file
|
|
and, for MEMORY.md, `details.rebrand_skipped_block_count` so you can
|
|
reword the relevant lines by hand.
|
|
|
|
### SOUL.md / USER.md / AGENTS.md Conflict Handling
|
|
|
|
These persona files are identity definitions (not additive like memory), so
|
|
when the destination already holds real user-curated content the migrator
|
|
asks you which version to keep instead of either silently dropping the
|
|
imported content or clobbering the existing file.
|
|
|
|
Use ``--persona-conflict`` to control the behavior:
|
|
|
|
| Mode | Behavior |
|
|
| --- | --- |
|
|
| ``prompt`` (default) | When stdin is a TTY: prompt for each conflicting file with a side-by-side preview and the four choices below. When stdin is not a TTY (CI, pipe, ``--json``): fall back to ``use-opensquilla`` and record a note so the choice is visible in the report. |
|
|
| ``use-opensquilla`` | Keep the destination file untouched. The OpenClaw original is copied to ``<output_dir>/archive/files/openclaw-orphaned/<filename>`` for review so nothing is silently lost. ``status: skipped``, ``details.persona_conflict_resolution: "use-opensquilla"``. |
|
|
| ``use-openclaw`` | Back up the destination to ``<name>.backup.<timestamp>`` and replace it with the OpenClaw content. ``status: migrated``, ``details.persona_conflict_resolution: "use-openclaw"``. |
|
|
| ``merge`` | Back up the destination and append the OpenClaw content below it under a ``## Imported from OpenClaw`` separator. Useful when the two versions are complementary rather than conflicting. ``status: migrated``, ``details.persona_conflict_resolution: "merge"``. |
|
|
| ``skip`` | Leave both files alone. The OpenClaw original is *not* archived — use ``use-opensquilla`` instead if you want a recoverable copy. ``status: skipped``, ``details.persona_conflict_resolution: "skip"``. |
|
|
|
|
``--overwrite`` short-circuits all of this and replaces the destination
|
|
wholesale (still with an item-level backup).
|
|
|
|
The pristine bootstrap-template case described below is handled before
|
|
``--persona-conflict`` and never asks for input: a freshly initialised
|
|
OpenSquilla workspace where the template is still untouched is treated as
|
|
overwrite-safe.
|
|
|
|
### MEMORY.md Merge Semantics
|
|
|
|
OpenClaw memory is additive by nature: every imported daily-memory file is
|
|
its own ``## Imported daily memory: <name>`` section. The OpenClaw migrator
|
|
therefore handles ``MEMORY.md`` differently from other workspace files: it
|
|
will never silently overwrite existing user-curated memory and it will
|
|
never silently drop the imported memory either.
|
|
|
|
Behaviour matrix (without ``--overwrite``):
|
|
|
|
| Destination state | What happens |
|
|
| --- | --- |
|
|
| ``MEMORY.md`` does not exist | Imported memory is written fresh. |
|
|
| Pristine OpenSquilla bootstrap template | Template is backed up, imported memory replaces it. ``details.replaced_bootstrap_template: true``. |
|
|
| Real user-curated content | Imported blocks that are not already present (after a whitespace-normalised, header-stripped comparison) are appended below the existing content. The pre-existing file is backed up first. ``details.appended_to_existing: true``, ``new_blocks_appended: N``, ``deduplicated_blocks_vs_existing: M``. |
|
|
| All imported blocks already present | The file is left untouched. ``status: skipped, reason: "all openclaw memory blocks already present in destination"``, ``details.deduplicated_against_existing: true``. No backup created. |
|
|
|
|
``--overwrite`` is the explicit "replace, do not merge" escape hatch — the
|
|
destination is backed up and replaced wholesale regardless of its current
|
|
contents.
|
|
|
|
### OpenSquilla Bootstrap-Template Handling
|
|
|
|
`ensure_agent_workspace` seeds placeholder ``SOUL.md`` / ``USER.md`` /
|
|
``AGENTS.md`` / ``MEMORY.md`` files when an OpenSquilla home is first
|
|
initialised. Without special handling those placeholders would block every
|
|
workspace-file migration with a silent ``conflict: target exists`` —
|
|
including the imported daily memory the user is migrating for in the first
|
|
place.
|
|
|
|
The OpenClaw migrator detects a destination that still holds the pristine
|
|
bootstrap template (byte-identical to the shipped placeholder after a
|
|
trailing-whitespace normalisation) and treats it as overwrite-safe:
|
|
|
|
- The pristine template is backed up to
|
|
``<name>.backup.<timestamp>`` next to the destination so the placeholder
|
|
guidance can be recovered on demand.
|
|
- The imported content replaces the template.
|
|
- The migration report marks the item with
|
|
``details.replaced_bootstrap_template: true`` so the special case is
|
|
visible rather than silent.
|
|
|
|
A destination file that the user has truly edited (i.e. no longer matches
|
|
the canonical template byte-for-byte) still gets the normal
|
|
``status: conflict`` treatment — only the pristine placeholder is treated
|
|
as overwrite-safe. To accept user edits being overwritten as well, pass
|
|
``--overwrite``.
|
|
|
|
### OpenClaw Limits
|
|
|
|
Some OpenClaw runtime behavior is not fully mapped yet:
|
|
|
|
- WhatsApp and Signal settings are detected, but OpenSquilla does not yet create
|
|
native migrated channel entries for them.
|
|
- Some advanced MCP fields such as headers/auth/cwd/include/exclude are not
|
|
native mapped.
|
|
- Some gateway, session, browser, approval, logging, plugin, cron, hook, memory
|
|
backend, skills registry, and UI settings are archived rather than applied.
|
|
- OpenSquilla does not widen channel privileges: ordinary OpenClaw allowlists
|
|
are not treated as OpenSquilla admin senders.
|
|
|
|
Review `MIGRATION_NOTES.md` after an applied migration for partial mappings and
|
|
manual follow-up.
|
|
|
|
## Hermes Agent -> OpenSquilla
|
|
|
|
Use this path if your existing agent state is in a Hermes Agent home.
|
|
|
|
Preview first:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --json
|
|
```
|
|
|
|
Preview a custom Hermes Agent home:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --source /path/to/.hermes --json
|
|
```
|
|
|
|
Preview a Hermes profile:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --profile work --json
|
|
```
|
|
|
|
Apply without secrets:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --apply
|
|
```
|
|
|
|
Apply and copy recognized secrets:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --apply --migrate-secrets
|
|
```
|
|
|
|
Apply and rename imported skill conflicts instead of skipping them:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --apply --skill-conflict rename
|
|
```
|
|
|
|
### What Is Migrated From Hermes Agent
|
|
|
|
OpenSquilla currently maps the common Hermes Agent home surface:
|
|
|
|
- Persona and user data files such as `SOUL.md`, `MEMORY.md`, and `USER.md`.
|
|
- Hermes skills, imported under `~/.opensquilla/skills/hermes-imports/`.
|
|
- Hermes model/provider config where there is an OpenSquilla-native equivalent.
|
|
- Hermes custom providers with `base_url`, mapped to OpenAI-compatible provider config.
|
|
- Environment values and recognized provider keys when `--migrate-secrets` is set.
|
|
- Search config where supported.
|
|
- MCP server definitions where supported.
|
|
- Telegram, Discord, and Slack channel tokens when `--migrate-secrets` is set.
|
|
- Selected plugin, cron, and unsupported runtime artifacts are archived for review.
|
|
|
|
### Hermes Agent Limits
|
|
|
|
The Hermes Agent migrator is newer than the OpenClaw migrator and has a smaller
|
|
coverage surface. Review the dry-run report carefully before applying.
|
|
|
|
Current limits:
|
|
|
|
- Live runtime state, active sessions, process state, and gateway state are not imported.
|
|
- Some Hermes runtime config option ids are accepted but currently deferred:
|
|
`workspace-files`, `tools-config`, `browser-config`, `session-config`,
|
|
`gateway-config`, `approvals-config`, `logging-config`, and `memory-backend`.
|
|
Each appears in the migration report as `status: deferred` with reason
|
|
`handler not implemented yet`. Selecting them via `--include` is not an
|
|
error; the migrator just records the gap so it is visible.
|
|
- Browser, tool, session, gateway, approval, and logging settings may require manual review.
|
|
- A full pre-apply snapshot of `~/.opensquilla` is not created automatically.
|
|
|
|
### Hermes Agent Migration Behavior
|
|
|
|
The Hermes migrator now mirrors the OpenClaw migrator on a few correctness
|
|
behaviors that were previously documented but not implemented:
|
|
|
|
- **Item-level backups.** When `--overwrite` replaces an existing
|
|
workspace file (`SOUL.md`, `MEMORY.md`, `USER.md`) or skill directory, the
|
|
prior contents are written to `<name>.backup.<timestamp>` next to the
|
|
original before the new content is applied.
|
|
- **Semantic deduplication on merge.** Existing destination content is split
|
|
into paragraph blocks and compared after whitespace normalization. A new
|
|
source body is appended unless an equivalent block already exists. The
|
|
previous naive substring check could silently drop short source bodies.
|
|
- **Memory overflow archival.** If the merged `MEMORY.md` would exceed
|
|
OpenSquilla's per-file size limit, the overflow is split at a paragraph
|
|
boundary and archived to
|
|
`~/.opensquilla/migration/hermes/<timestamp>/archive/memory-overflow/MEMORY.overflow.md`.
|
|
A short pointer is left at the end of `MEMORY.md`.
|
|
- **Branding rewrite.** Hermes branding in imported workspace prose
|
|
(`SOUL.md`, `MEMORY.md`, `USER.md`) is rewritten to OpenSquilla. Bare
|
|
`Hermes` is only rewritten when it is followed by a workspace-context word
|
|
(e.g. `home`, `workspace`, `memory`, `config`). Source-reference tokens
|
|
such as `HERMES_HOME`, `NousResearch`, and `hermes-agent` are preserved so
|
|
the migration archive still points back at the original source. The
|
|
unrebranded original is copied to
|
|
`<output_dir>/archive/files/workspace-original/<name>.md` for review.
|
|
- **Path-token replacement is word-boundary aware.** Previously a plain
|
|
string replace turned `~/.hermesrc` into `~/.opensquillarc` and
|
|
`.hermes_backup` into `.opensquilla_backup` — meaningless paths. The
|
|
rebrand now only rewrites `.hermes` when it ends a path token (i.e.
|
|
followed by `/`, whitespace, quote, or end-of-string). Same rule
|
|
applied to `.openclaw` on the OpenClaw side, and bare-word
|
|
`OpenClaw` / `openclaw` are now matched with `\b` so substrings like
|
|
`OpenClawFlavored` or `openclaw_pid` are left alone.
|
|
- **Non-UTF-8 source files no longer crash.** Hand-edited source files
|
|
with stray bad bytes (CP1252 fragments, leftover binary paste, etc.)
|
|
used to abort the entire Hermes migration with
|
|
`UnicodeDecodeError`. The source read now uses `errors="replace"`
|
|
(matching OpenClaw); offending bytes become U+FFFD so users can spot
|
|
them.
|
|
- **`mcp.enabled = false` is no longer silently flipped.** When the
|
|
destination home already has MCP servers AND `mcp.enabled = false`
|
|
(an explicit "I don't want MCP right now" choice), importing more
|
|
MCP servers leaves the flag at `false` and surfaces
|
|
`details.mcp_enabled_left_disabled` plus a `manual_steps` hint. MCP
|
|
is still flipped on automatically when the destination had no
|
|
servers (framework default — flipping is what the user wants).
|
|
- **Mixed-subject prose is kept verbatim.** Workspace notes often
|
|
describe Hermes AND OpenSquilla as distinct entities ("Hermes Agent
|
|
v0.13.0 installed at ~/.local/bin/hermes; OpenSquilla also installed
|
|
at ~/.local/bin/opensquilla. Has `migrate hermes` subcommand."). A
|
|
mechanical rebrand collapses the two subjects into one and produces
|
|
factual errors (path mismatches), tautologies ("OpenSquilla skills
|
|
loadable by OpenSquilla"), and self-referential commands ("migrate
|
|
OpenSquilla skills to OpenSquilla"). When the source already
|
|
mentions `OpenSquilla` (any case), the migrator now skips rebrand
|
|
for that file and writes it verbatim, recording
|
|
`details.rebrand_skipped: "mentions-opensquilla"` so you can decide
|
|
which mentions to reword by hand. The same rule applies per-block
|
|
during MEMORY.md merging; the count of skipped blocks is reported
|
|
via `details.rebrand_skipped_block_count`.
|
|
- **Skill compatibility reporting.** Each imported skill's
|
|
report record now includes `details.compatibility` (`loadable` /
|
|
`needs_review` / `not_loadable`) and `details.compatibility_issues` listing
|
|
missing frontmatter, oversize bodies, or invalid YAML. Skills are still
|
|
copied; the field is informational so you can find ones that may need
|
|
attention before activating.
|
|
- **Unknown providers are no longer written to `llm.provider`.** Hermes
|
|
uses values like `auto` (runtime auto-detect) and may ship experimental
|
|
providers (`bedrock`, `ollama`, ...) that have no OpenSquilla equivalent.
|
|
Writing them verbatim used to crash `persist_config` because OpenSquilla
|
|
validates `llm.provider` against a known set AND requires
|
|
`squilla_router.tier_profile` to agree with it. The migrator now leaves
|
|
`llm.provider` untouched in that case; the model id and base URL are
|
|
still migrated, and the model-config item carries
|
|
`details.unrecognized_provider`, `details.llm_provider_left_unchanged`,
|
|
and a `manual_steps` hint explaining how to set the provider explicitly.
|
|
- **Known providers that clash with an existing
|
|
`squilla_router.tier_profile` are also not written.** Even when the
|
|
Hermes provider is recognized (e.g. `anthropic`), persisting it would
|
|
fail if the destination home already pins `squilla_router.tier_profile`
|
|
to a different provider (e.g. `openrouter`) — OpenSquilla requires the
|
|
two to match. The migrator now detects the clash, leaves `llm.provider`
|
|
unchanged, and records `details.tier_profile_conflict`,
|
|
`details.llm_provider_left_unchanged`, and a `manual_steps` hint so you
|
|
can switch providers explicitly via `opensquilla config set` or by
|
|
clearing `squilla_router.tier_profile` first.
|
|
- **MCP server entries upsert instead of replacing.** Both migrators
|
|
used to assign `cfg.mcp.servers = imported`, silently destroying any
|
|
pre-existing OpenSquilla MCP servers the user already had configured.
|
|
Now the imported servers are upserted by name: same-name entries are
|
|
replaced (the imported version wins), unrelated entries are preserved.
|
|
The `mcp-servers` report record carries `details.added`,
|
|
`details.replaced`, and `details.preserved_existing`.
|
|
- **Resilient SKILL.md compatibility check.** A SKILL.md with empty or
|
|
non-dict YAML frontmatter (e.g. `---\n\n---`) used to crash the whole
|
|
migration with `AttributeError`. The check now records the skill as
|
|
`compatibility: "not_loadable"` and continues. The reported
|
|
`compatibility` string is also kept consistent with the
|
|
`opensquilla_loadable` boolean.
|
|
|
|
## Reports
|
|
|
|
Use `--json` for dry-run automation:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --json
|
|
opensquilla migrate hermes --json
|
|
```
|
|
|
|
Applied migrations write report files under:
|
|
|
|
```text
|
|
~/.opensquilla/migration/openclaw/<timestamp>/
|
|
~/.opensquilla/migration/hermes/<timestamp>/
|
|
```
|
|
|
|
Typical files:
|
|
|
|
- `report.json`: structured item-level report.
|
|
- `summary.md`: human-readable count summary.
|
|
- `MIGRATION_NOTES.md`: OpenClaw migration notes when semantic conversions or
|
|
partial mappings are present.
|
|
- `archive/`: unsupported or review-only artifacts.
|
|
|
|
Hermes dry runs also write report files. OpenClaw dry runs are best inspected
|
|
with `--json`; apply mode writes the report files.
|
|
|
|
## Validate After Migration
|
|
|
|
After applying a migration, start the gateway and run a small chat check:
|
|
|
|
```sh
|
|
opensquilla gateway start --json
|
|
opensquilla chat
|
|
```
|
|
|
|
Or use a one-shot prompt:
|
|
|
|
```sh
|
|
opensquilla agent -m "Briefly summarize your active persona and available memory."
|
|
```
|
|
|
|
Also check:
|
|
|
|
- `~/.opensquilla/workspace/` for migrated persona and memory files.
|
|
- `~/.opensquilla/skills/openclaw-imports/` or `~/.opensquilla/skills/hermes-imports/`.
|
|
- `~/.opensquilla/migration/<source>/<timestamp>/summary.md`.
|
|
- `~/.opensquilla/migration/<source>/<timestamp>/MIGRATION_NOTES.md` when present.
|
|
|
|
If behavior does not look right, stop the gateway, review the migration report,
|
|
and re-run with a narrower `--preset`, `--include`, or `--exclude` selection.
|
|
|
|
## Examples
|
|
|
|
Migrate only user data from OpenClaw:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --preset user-data --apply
|
|
```
|
|
|
|
Migrate only Hermes skills and persona files:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --include soul,skills --apply
|
|
```
|
|
|
|
Preview OpenClaw migration while excluding channel settings:
|
|
|
|
```sh
|
|
opensquilla migrate openclaw --exclude telegram-settings,discord-settings,slack-settings --json
|
|
```
|
|
|
|
Apply Hermes migration to a custom config file:
|
|
|
|
```sh
|
|
opensquilla migrate hermes --config /path/to/opensquilla.toml --apply
|
|
```
|