DeepSeek Harness plugin

dsh-migrate

Import chat history and memory from other AI agents (Claude Code, Codex CLI, Gemini CLI, Aider, Cursor, Cline, OpenCode, …) into DeepSeek Harness

Jump to install

Source facts

Repository
kkkkkkkkki/dsh-migrate
Latest update
Aug 16, 2026
Category
Tools & Capabilities
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/kkkkkkkkki/dsh-migrate
Plugin: dsh-migrate
Author: kkkkkkkkki

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-migrate

English | 中文

![npm version](https://www.npmjs.com/package/@ersss/dsh-migrate) ![License: MIT](LICENSE) ![Node.js ≥ 22.13](package.json) ![Awesome DSH Plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)

Import chat history and memory from other AI agents into DeepSeek Harness.

Switching harnesses should not mean losing your context. dsh-migrate reads the transcripts and memory stores other agents leave on disk and writes them as native DeepSeek Harness session logs — searchable, inspectable in the Trajectory view, and resumable (--fork-style continue) as if they had been recorded by DSH itself.

It works two ways: a /migrate slash command inside DSH, and a standalone CLI (npx @ersss/dsh-migrate) that scans the whole machine and previews with a dry run before writing anything.

Supported sources

AgentSessionsMemory
Claude Code~/.claude/projects/**.jsonl (text, thinking, tool_use/tool_result)~/.claude/CLAUDE.md, project memory/*.md
Codex CLI~/.codex/sessions/**/rollout-*.jsonl (messages, reasoning, function calls)~/.codex/AGENTS.md, instructions.md
Gemini CLI~/.gemini/tmp/**/chats/session-*.json (messages, thoughts, toolCalls)~/.gemini/GEMINI.md
Cursor~/.cursor/projects/*/agent-transcripts/*/*.jsonl (text, tool_use; results live in Cursor's bubble store and are not recorded)
Kimi CLI~/.kimi/sessions/*/*/wire.jsonl (text, thinking, tool calls; cwd recovered via kimi.json)
ChatGPT (web export)conversations.json from the data export (DAG-mapped threads; tool messages folded to text)
Cline / Roo CodeVS Code globalStorage/*/tasks/*/api_conversation_history.json
OpenCode~/.local/share/opencode/** message/part JSON files
Aider**/.aider.chat.history.md (bounded scan)CONVENTIONS.md files
Hermes Agent~/.hermes/state.db (SQLite sessions+messages, read-only)~/.hermes/memories/MEMORY.md, USER.md
OpenClaw~/.openclaw/agents/*/agent/openclaw-agent.sqlite (transcript_events, read-only)workspace MEMORY.md, memory/*.md

Every adapter is tolerant: malformed lines and unknown row types are skipped, never fatal. Missing fields degrade gracefully (unknown model → imported, unknown cwd → the _no-cwd project).

Install

# 1. Install the plugin into your DSH web profile
dsh plugin --profile web add @ersss/dsh-migrate

# 2. Restart DSH
dsh web

The package declares a dsh.bundle patch layer, so installing it does two things at once:

  • registers the /migrate slash command, and
  • points DSH's session store at $DSH_HOME/sessions-imported in raw-JSONL mode — a dedicated root for imported logs that never mixes with the default store's encoding.

> Heads-up: the bundled patch relocates the default session root, which means sessions you recorded before installing the plugin stay in the old sessions/ root and stop being listed. If you have existing DSH sessions you still want listed, either merge the patch's two rows into $DSH_HOME/profiles/web/cordis.patch.yml and keep both roots by alternating --patch, or simply run the CLI with --out pointed at your live root (raw JSONL reads fine under a zstd root only if the root was created with compression: 'none'). Fresh installs are unaffected.

Use

From inside DSH (Web UI, TUI, headless):

/migrate list                 # show what each agent has on disk
/migrate run                  # import everything
/migrate run --source claude-code --limit 10

From a terminal, without DSH running (also the right choice for big archives):

npx @ersss/dsh-migrate list
npx @ersss/dsh-migrate run                       # → $DSH_HOME/sessions-imported
npx @ersss/dsh-migrate run --out /path/to/root   # anywhere you like
npx @ersss/dsh-migrate run --write-instructions .  # also fold memory into ./AGENTS.md

Imported sessions appear in the sidebar grouped by their original project directory, and in the Trajectory view with their full event stream — prompts, reasoning, tool calls, and results.

What gets written

DSH's session log is an append-only JSONL event stream (SESSION_FORMAT_VERSION 0). For every source session, dsh-migrate writes one log:

$DSH_HOME/sessions-imported/--<project>--/<id>/session.jsonl

  {"type":"session","version":0,"id":"…","createdAt":…,"delegationDepth":0,"cwd":"…"}
  {"type":"turn/start","seq":0,"time":…,"data":{"turn":1}}
  {"type":"user/message","seq":1,…,"data":{"role":"user","source":{"kind":"user","via":"dsh-migrate:claude-code"},"content":[{"type":"text","text":"…"}]},"surfaceOp":"append"}
  {"type":"step/start",…} {"type":"assistant/message",…} {"type":"tool/call",…} {"type":"tool/result",…} {"type":"step/end",…}
  {"type":"turn/end",…,"data":{"turn":1,"reason":{"kind":"completed"}}}
  …
  {"type":"session/end-seed",…}        ← marks the log as imported seed history

Mapping decisions, briefly:

  • Turns. One source user prompt (plus the assistant work it triggered) becomes one turn/start…turn/end bracket. Tool-result-only user rows (Claude Code, Cline) attach to the pending tool/call instead of opening a turn.
  • Messages. User and system text become user/message events; assistant text/reasoning/tool-calls become one assistant/message per step; tool results become tool/result paired by call id.
  • Provenance, not impersonation. Imported assistant messages keep the original provider and model (provider: "dsh-migrate:anthropic", model: "claude-fable-5"). Imported user messages are kind: "user" — required for the trajectory view and history derivation — with the importer declared on the merge-extensible via channel (via: "dsh-migrate:claude-code").
  • Timestamps and ids. Original timestamps are preserved when the source records them (monotonicity enforced); missing call ids get fresh UUIDs; session ids are fresh UUIDs (never the source's) so an import can never shadow a live session. The header carries importedSource / importedSourceId, so imported sessions are filterable without walking the event stream.
  • Memory. Each source's memory records become one "Imported memory" session (readable, searchable, resumable), and --write-instructions <project> additionally folds them into that project's AGENTS.md between <!-- dsh-migrate … --> markers (idempotent re-runs).
  • Append-only honesty. Writes use wx (no overwrite). A colliding id is retried with a fresh one; existing logs are never modified.

How resume works

The trailing session/end-seed event makes the whole imported log seed history: when you continue an imported session, DSH replays the log as inherited context, then starts a clean live segment — the model sees the prior conversation exactly as DSH would have logged it. The agent preset and tools of the current runtime apply; tools the old agent had and DSH does not simply become history.

Notes & limitations

  • Read-only on sources. The importer never writes to the source agents' directories.
  • Images and attachments are not imported (DSH attachments are content-addressed host objects); their surrounding text is.
  • Compaction history in source transcripts is flattened to plain messages.
  • Source-specific gaps, by design of the source format: Cursor transcripts don't record tool results (calls import without results); ChatGPT exports have no structured tool calls (tool messages become text); Kimi CLI sub-agent internals mirrored as SubagentEvent are skipped (import subagents/<id>/wire.jsonl directly if you want them).
  • SQLite sources are read immutably (mode=ro&immutable=1), so a running Hermes/OpenClaw gateway is never disturbed. OpenClaw imports every session_window; resets/rollover windows appear as separate sessions (they were separate transcripts upstream too).
  • Aider scan roots are $DSH_MIGRATE_SCAN (path-list), ~/projects, ~/code, ~/dev, ~/work, and a shallow $HOME sweep — its history files live inside projects, so a fixed home-relative path cannot find them.
  • Format drift: these layouts are undocumented internals of the source agents and change over time. Adapters skip what they do not recognise; if your agent version is newer, please open an issue with a (redacted) sample row.

Development

npm install
npm run build      # tsc → dist/
npm test           # fixture-driven end-to-end tests (real DSH codec replay)

The test suite replays produced logs through the actual @deepseek-ai/dsh-session codec (decodeStorageRecord + foldSurface) vendored from a live DSH profile, so format regressions fail loudly.

Contributing a source adapter

One adapter = one file in src/formats/ implementing:

interface SourceAdapter {
  key: string
  label: string
  detect(home: string): Promise<DiscoveredSource | undefined>
  readSessions(home: string, options?: { limit?: number }): Promise<IrSession[]>
  readMemories(home: string): Promise<IrMemory[]>
}

Register it in src/formats/index.ts and add a fixture test. See src/formats/claude-code.ts for the reference implementation.

License

MIT