Migrate Claude Code, Codex, OpenCode and Hermes into DeepSeek Harness — copy sessions, memories, skills, instructions and slash commands as resumable DSH sessions, copy-only and approval-gated.
Keep your Claude Code history when you move: one install, resumable sessions, live sync with a running Claude Code, and a four-source migration wizard.
- Targets
dsh 0.1.0-rc.6(web profile); peer dependencies pinned to0.1.0-rc.6. Node^22.19 || >=24. - Last verified against a fresh tarball install: real scan, real batch import (idempotent re-import), workspace attach and persistence artifacts confirmed; macOS/Linux covered by the CI matrix.
| Surface | Used | Fallback when absent |
|---|---|---|
Host services (tools / sessionPersistence / workspaceRegistry / commands / systemPrompt / skills / webServer) |
required where listed | optional services register reactively; missing fs fails loud |
sessionPersistence.listSnapshots / readFrom / streamText-capable fs / ctx.jobs / ctx.agents.resume |
feature-detected | list() / whole-file read with loud rejection / own job map / handoff inject |
Client shell services (sessions.refresh/open, workspaces.refresh) |
feature-detected at panel apply | full-page reload |
| Newer platform capabilities are never hard requirements — the plugin stays bootable on rc.6. |
- Auto-discovery —
claude_scanlocates the Claude data root ($CLAUDE_CONFIG_DIR, fallback~/.claude) and indexes every project/session, memory, skill, globalCLAUDE.mdandsettings.json, with incremental caching and parallel scanning (scanConcurrency). - Full-fidelity import —
import_claudeturns transcripts into balanced, resumable DSH sessions (turn/start → step/start → user/message → assistant/message → tool/call → tool/result → step/end → turn/end), repairs interrupted tool calls, and stream-imports transcripts larger thanmaxTranscriptBytesin chunks. - One
claudecodeworkspace — every imported session lands in a dedicated workspace (default$DSH_HOME/claudecode);workspaceMode: 'per-project'restores one-workspace-per-project grouping. - Copy-only & incremental — nothing on either side is moved, rewritten, or deleted; re-running appends only the new turns (
force: truesaves an extra full copy under a new id). - Personal context, always fresh — memories injected as a live prompt section, Claude skills registered as real DSH skills (global + project-level), global + project
CLAUDE.mdinjected early. - Four-source migration wizard —
/moveplusmove_detect/move_preview/move_runmigrate Claude Code, Codex, OpenCode and Hermes, approval-gated and idempotent (move.json). - Web panel & commands —
/claude-import-all,/resume-claude,/claude-move-reset, and a floating migration panel.
/move # one-shot wizard: detect → preview → execute → report (all four sources)
move_detect # scan Claude Code / Codex / OpenCode / Hermes
move_preview # per-item plan: new | unchanged | changed | conflict (with diff) | unsupported
move_run # execute behind the approval gate; conflict resolution:
# skip | overwrite | rename | merge (default skip — never guesses)
- Sources — Claude Code (
~/.claude), Codex (~/.codex), OpenCode (data + config roots), Hermes (skills/memory roots); each source has its own parser + mapper. - Mapping — memories/instructions → append-only managed sections in the DSH global
AGENTS.md(one marked section per item); skills → real DSH skills (SKILL.mdbundles copied verbatim, other formats converted); slash commands → registered DSH commands (rebuilt frommove.jsonafter a restart); sessions → resumable DSH sessions (the same importers as phase 1). - Idempotent — every applied plan is recorded in
$DSH_HOME/claude-move/move.json(digest/targetDigest/appliedAt); re-runs skip unchanged items andforcere-applies them. - Approval-gated — a run that would write anything asks
ctx.approvalfirst; anything butallowed-oncemeans zero writes.
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-claude-move#master"
# or from npm (published releases)
dsh plugin --profile web add dsh-claude-move
# 2. restart and verify the row
dsh --profile web --dump-config | grep -A4 'id: claude-move'Then, in any DSH session, run one command:
/claude-import-all # scan → copy every Claude session → reportNo DSH restart is needed after importing — refresh the open Web page once and click any imported session to continue.
- git channel (latest
master):dsh plugin --profile web add "github:PerryLink/dsh-claude-move#master"— pure ESM, noprepareorallowBuildsstep. - npm channel (published releases):
dsh plugin --profile web add dsh-claude-move. - tarball channel:
npm packin this repo, thendsh plugin --profile web add ./dsh-claude-move-<version>.tgz. - uninstall: remove the
claude-moverow from the profile's bundles and restartdsh. Imported sessions stay in DSH's data directory; the plugin only writes its cache ($DSH_HOME/claude-move/) and theclaudecodeworkspace folder, and never touches Claude source data.
~/.claude (read-only)
├─ projects/*/*.jsonl ──→ resumable DSH sessions, grouped in one "claudecode" workspace (default)
├─ projects/*/memory/ ──→ live system-prompt memory section (re-read per request)
├─ skills/** ──→ real DSH skills
└─ CLAUDE.md + settings ──→ early prompt section + config suggestions (never auto-applied)
| In Claude Code | Lands in DSH as |
|---|---|
Session transcripts (projects/*/*.jsonl) |
Balanced, resumable DSH sessions — full-fidelity user/assistant/tool/thinking mapping with interrupted-tool-call repair — grouped into one claudecode workspace or one per project |
Memory files (projects/*/memory/*.md) |
A live system-prompt context section, re-read on every request (feedback > project > reference > user) |
Skills (~/.claude/skills/**) |
Real DSH skills (kebab-case names, collision suffixes, max 30 by default; README.md/MEMORY.md and files without a description are skipped) |
CLAUDE.md (global + per-project) |
An early prompt section; the project file wins |
settings.json |
DSH configuration suggestions with an explicit unmappable-keys list |
| Project state (directory, git branch & dirty count) | Shown in the scan index, the Web panel badges, and the /resume-claude handoff |
Call the tools in any session with the plugin mounted:
claude_scan # full scan (incremental cache)
claude_scan { path: "~/.claude/projects/<slug>" } # partial scan
claude_scan { refresh: true } # skip cache, rescan everything
claude_scan { projectsLimit: 10, sessionsLimit: 5, fields: "brief" } # trim output
import_claude { path: "~/.claude/projects/<slug>/<sessionId>.jsonl" } # one session
import_claude { path: "~/.claude/projects" } # directory (recursive)
import_claude { path: "all" } # everything
# Re-run any time: unchanged files are skipped, grown transcripts append only the new turns.
# Files over maxTranscriptBytes are stream-imported in chunks (no memory ceiling).
import_claude { path: "...", force: true } # fresh full copy (previous copy kept)
Commands (user-triggered, no model turn):
/claude-import-all # one-shot: scan → import everything → report → inject into the current session
/resume-claude latest # continue the most recent Claude session
/resume-claude <sessionId> # by source session id or import-<src> id
/resume-claude <keyword> # match titles; multiple matches are listed, never guessed
/claude-move-reset # reset the plugin cache (bookmarks + import map); imported sessions are kept
Web panel: a floating migration panel with the project/session tree, status badges (not imported / imported / imported-with-new-turns / source missing / directory missing / git dirty), keyword filter, paged rendering, per-session "Import & continue" + "Open session" + "Refresh session list", batch import with a live progress bar and cancel, and a cache-reset button. Texts follow the browser language (zh/en). Served through the plugin's own /api/claude-move/* JSON routes on the public ctx.webServer seam.
You do not need to restart DSH. Imports land durably through the public sessionPersistence service the moment they complete:
- The server-side lists (
session.list/workspace.listRPCs, the CLI, any new page load) show the imported sessions under theclaudecodeworkspace immediately. - The panel refreshes the already-open page's session list itself and offers an Open session button per imported session.
- Imported sessions can be opened, read, and resumed right away —
/resume-claude, or click the session in the list. Re-running the import at any time syncs only the new turns into the same sessions.
All optional, overridable in cordis.yml.
| Key | Default | Meaning |
|---|---|---|
claudeHome |
$CLAUDE_CONFIG_DIR or ~/.claude |
Claude data root |
workspaceMode |
claudecode |
claudecode (one dedicated workspace) · per-project (one workspace per source cwd) |
claudecodeDir |
$DSH_HOME/claudecode |
The claudecode workspace folder (the only folder the plugin ever creates) |
scanGit |
true |
Git probe level: true (full) · 'branch' (zero git calls) · false |
gitTimeoutMs |
5000 |
Git subprocess timeout |
scanConcurrency |
8 |
Parallel project scan cap |
maxTranscriptBytes |
67108864 |
Stream-import threshold (chunked above) |
excludeProjects |
[] |
Slug substrings to skip |
enableMemory |
true |
Inject memories as a live prompt section |
memoryMaxBytes |
8192 |
Memory section cap |
memoryScope |
current-project |
current-project · all (current first) |
enableSkills |
true |
Register Claude skills as DSH skills |
maxSkills |
30 |
Skill count cap |
extraSkillDirs |
[] |
Extra skill directories |
enableInstructions |
true |
Inject global + project CLAUDE.md |
resumeMaxChars |
2048 |
Handoff summary char cap |
resumeMode |
inject |
inject (handoff summary) · agents (ctx.agents.resume) |
enableWebPanel |
true |
Register the /api/claude-move/* panel routes |
importConcurrency |
4 |
Parallel read+convert per batch |
requireApproval |
true |
Wizard writes ask ctx.approval (allowed-once only) |
codexHome |
$CODEX_HOME or ~/.codex |
Codex data root |
opencodeDataHome |
platform XDG data dir/opencode | OpenCode data root |
opencodeConfigHome |
platform XDG config dir/opencode | OpenCode config root |
hermesHome |
$HERMES_HOME or ~/.hermes |
Hermes data root |
skillsDir |
$DSH_HOME/skills |
Wizard skill target |
agentsMdPath |
$DSH_HOME/AGENTS.md |
Wizard memory/instruction target |
moveWorkspaceMode |
per-source |
per-source · single workspace grouping for wizard imports |
| Surface | Kind | Notes |
|---|---|---|
claude_scan |
tool | Structured index of projects/sessions/memories/skills/settings |
import_claude |
tool | Import one session, a directory, or all (incremental, force for a fresh copy) |
move_detect / move_preview / move_run |
tools | Four-source wizard: scan, per-item plan with diffs, execute behind approval |
/claude-import-all |
command | Scan → import everything → report |
/resume-claude |
command | Continue a Claude session (latest, id, or keyword) |
/claude-move-reset |
command | Reset the plugin cache (imported sessions kept) |
/move |
command | One-shot four-source wizard |
| Web migration panel | client | Floating panel with progress, cancel, paging, open session |
- Permissions: the workshop manifest declares
filesystem:readandfilesystem:write. - Reads
~/.claude(transcripts, memories, skills,CLAUDE.md,settings.json) — strictly read-only — and the project directories it imports into. - Writes DSH session logs via the public
sessionPersistenceservice (create + append only, never delete/rewrite/archive), workspace-registry records, its cache under$DSH_HOME/claude-move/, and theclaudecodeworkspace folder. - Never modifies Claude source files, touches other applications' data, or accesses the network. No credentials are read or transmitted.
- Source files are read-only; DSH logs are append-only (
create+appendonly). - External transcripts are untrusted input — nothing in them is executed; system/developer/thinking content never enters the resume handoff.
- Public services only —
sessionPersistence/workspaceRegistry/tools/commands/systemPrompt/skills/webServer; no engine or UI changes. - Secrets reported by position only (file:line:kind);
permission/permission-mode/queue-operationrecords are counted, not imported. - Wizard writes are approval-gated — anything but
allowed-oncemeans zero writes.
- Titles come from
custom-title/ai-title/first prompt; Claudesummaryrecords are reported but not mapped to DSH compaction nodes (synthesizing a valid compaction transaction would fabricate its seq range and checkpoint message). thinkingblocks are kept asreasoningcontent but never enter the resume handoff.- Interrupted tool calls are repaired with a synthetic error result (never dropped), reported as
repaired.synthesized. - Permission-class records are counted, not imported; DSH permission-preset suggestions are generated in reports.
- On hosts without a streaming
fs.streamTextsurface, transcripts larger thanmaxTranscriptBytesfail loudly instead of partial import. - In
workspaceMode: 'per-project', sessions whose source directory was deleted still import, but workspace attach fails (left ungrouped;workspace.attached: falseplus areason). The defaultclaudecodeworkspace does not depend on the source directory. - If a transcript was truncated or reset in place (fewer turns than the recorded import), re-import skips it and reports
sourceShrunk; useforce: truefor a fresh full copy. - The Web panel is a zero-build floating panel driven by the plugin's own JSON routes; it does not use the shell's internal UI slot system.
- The model-facing surface is the two tools' descriptions/schemas and their outputs:
claude_scanreturns the structured index,import_claudereturns per-file summaries with positions of warnings. Tool results are themselves loggedtool/resultevents, so everything is reconstructable. - No hidden model-facing text; memory/
CLAUDE.mdsections are registered onctx.systemPrompt(prompt assembly, rebuildable from the session log).
- Row not effective:
dsh --profile <p> --dump-configshould print# == dsh-claude-move; re-rundsh plugin --profile <p> add .... - Web boots but hangs silently: new profiles initialized by
dsh plugin addcontain onlydsh-base— add@deepseek-ai/dsh-web-apptodsh.profile.bundles. Installing into the existingwebprofile needs nothing. - Panel routes 404: they are served only when
enableWebPanel: trueand a web server is composed; check the boot log for FAILED fibers. - Import fails with "transcript 过大": raise
maxTranscriptBytesor import that file individually. - Import succeeded but the sidebar shows no new session: the page was already open — click the panel's refresh button (or reload the page) once. No DSH restart is ever needed.
- Logs: boot failures print to the
dshconsole; the plugin logs[claude-move]-prefixed errors for workspace/import-map issues.
This project is licensed under the Apache License 2.0; the following MIT-licensed components retain their own licenses (full text in THIRD_PARTY_NOTICES.md):
- Conversion core vendored from Nwflower/dsh-chat-import (MIT).
- Discovery conventions & safety model from Demogorgon314/dsh-resume-plugin (MIT).
- Memory/skills injection & frontmatter parsing patterns from YYTbit/dsh-plugin-claude-bridge (MIT).
npm install # peer deps: @deepseek-ai/dsh-tools@0.1.0-rc.6, @deepseek-ai/cordis, schemastery
npm test # node --test test/*.test.mjsCI runs the full suite on Node 22 across Linux/macOS/Windows via GitHub Actions (test.yml).
deepseek-harness, dsh-plugin, claude-code, migration, session-import, resume
- @PerryLink — creator and maintainer: the import pipeline, the four-source migration wizard, the Web panel, docs, CI/CD and releases.
- @OLDnana1 — root-cause analysis of the interrupted tool-call corruption that made imported sessions permanently return HTTP 400 on resume.
- @GooodWei — identified
README.md(and any description-less.md) being misregistered as a skill, which broke DSH's skill load.
This project is one of the DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:
| Plugin | One-liner |
|---|---|
| dsh-mcp-panel | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors |
| dsh-doublecheck | Engineering-discipline guard: requirements grill, test gates, adversary review |
| dsh-background-agents | Durable background child agents with a Web UI sidebar, messaging and interrupt |
| dsh-lsp-actions | LSP diagnostics, formatting, completion, code actions and rename over language servers |
| dsh-output-styles | Claude Code outputStyles-equivalent runtime style switching |
| dsh-checkpoint-rewind | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore |
| dsh-permission-rules | Claude Code-style declarative allow/deny/ask permission rules with audit |
| dsh-auto-review | Second-model auto-review on the approval chain, fail-closed by default |
| dsh-memento | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
| dsh-skill-pack-security | Security-audit skill pack: secret scan, dependency and supply-chain review |
| dsh-session-pin | Pin sessions in the Web sidebar with durable ordering |
| dsh-composer-history | Terminal-style input history for the web composer: arrows, Ctrl+R search |
| dsh-github | GitHub PR/issues integration for DSH, every write gated by approval |
| dsh-plugin-guide | Plugin-development knowledge base as an on-demand agent skill |
| dsh-claude-move | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
Apache License 2.0 © 2026 dsh-claude-move contributors