# ๐Ÿšš dsh-claude-move **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.* [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![DSH plugin](https://img.shields.io/badge/dsh-plugin-โœ…-green)](https://github.com/topics/dsh-plugin) [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#) [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-claude-move/test.yml?branch=master&label=CI)](https://github.com/PerryLink/dsh-claude-move/actions) [![Version](https://img.shields.io/github/v/tag/PerryLink/dsh-claude-move?label=version)](https://github.com/PerryLink/dsh-claude-move/releases) [![npm version](https://img.shields.io/npm/v/dsh-claude-move)](https://www.npmjs.com/package/dsh-claude-move) [![npm downloads](https://img.shields.io/npm/dm/dsh-claude-move)](https://www.npmjs.com/package/dsh-claude-move) [English](README.md) ยท [็ฎ€ไฝ“ไธญๆ–‡](README.zh.md) ยท [Espaรฑol](README.es.md) ยท [Portuguรชs](README.pt.md) ยท [เคนเคฟเคจเฅเคฆเฅ€](README.hi.md)
--- ## Compatibility - Targets `dsh 0.1.0-rc.6` (web profile); peer dependencies pinned to `0.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. ### Compatibility matrix (public seams only) | 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. | | | ## What you get 1. **Auto-discovery** โ€” `claude_scan` locates the Claude data root (`$CLAUDE_CONFIG_DIR`, fallback `~/.claude`) and indexes every project/session, memory, skill, global `CLAUDE.md` and `settings.json`, with incremental caching and parallel scanning (`scanConcurrency`). 2. **Full-fidelity import** โ€” `import_claude` turns 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 than `maxTranscriptBytes` in chunks. 3. **One `claudecode` workspace** โ€” every imported session lands in a dedicated workspace (default `$DSH_HOME/claudecode`); `workspaceMode: 'per-project'` restores one-workspace-per-project grouping. 4. **Copy-only & incremental** โ€” nothing on either side is moved, rewritten, or deleted; re-running appends only the new turns (`force: true` saves an extra full copy under a new id). 5. **Personal context, always fresh** โ€” memories injected as a live prompt section, Claude skills registered as real DSH skills (global + project-level), global + project `CLAUDE.md` injected early. 6. **Four-source migration wizard** โ€” `/move` plus `move_detect` / `move_preview` / `move_run` migrate Claude Code, Codex, OpenCode and Hermes, approval-gated and idempotent (`move.json`). 7. **Web panel & commands** โ€” `/claude-import-all`, `/resume-claude`, `/claude-move-reset`, and a floating migration panel. ## Four-source migration wizard ```text /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.md` bundles copied verbatim, other formats converted); slash commands โ†’ registered DSH commands (rebuilt from `move.json` after 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 and `force` re-applies them. - **Approval-gated** โ€” a run that would write anything asks `ctx.approval` first; anything but `allowed-once` means zero writes. ## Quick start ```sh # 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: ```sh /claude-import-all # scan โ†’ copy every Claude session โ†’ report ``` No DSH restart is needed after importing โ€” refresh the open Web page once and click any imported session to continue. ## Install & uninstall - **git channel** (latest `master`): `dsh plugin --profile web add "github:PerryLink/dsh-claude-move#master"` โ€” pure ESM, no `prepare` or `allowBuilds` step. - **npm channel** (published releases): `dsh plugin --profile web add dsh-claude-move`. - **tarball channel**: `npm pack` in this repo, then `dsh plugin --profile web add ./dsh-claude-move-.tgz`. - **uninstall**: remove the `claude-move` row from the profile's bundles and restart `dsh`. Imported sessions stay in DSH's data directory; the plugin only writes its cache (`$DSH_HOME/claude-move/`) and the `claudecode` workspace folder, and never touches Claude source data. ## What gets migrated ``` ~/.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 | ## Usage Call the tools in any session with the plugin mounted: ``` claude_scan # full scan (incremental cache) claude_scan { path: "~/.claude/projects/" } # 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//.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 # by source session id or import- id /resume-claude # 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. ## After importing **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.list` RPCs, the CLI, any new page load) show the imported sessions under the **`claudecode` workspace** 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. ## Configuration 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 | ## Tools & surfaces | 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 & data - **Permissions**: the workshop manifest declares `filesystem:read` and `filesystem: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 `sessionPersistence` service (create + append only, never delete/rewrite/archive), workspace-registry records, its cache under `$DSH_HOME/claude-move/`, and the `claudecode` workspace folder. - **Never** modifies Claude source files, touches other applications' data, or accesses the network. **No credentials** are read or transmitted. ## Security boundaries - **Source files are read-only; DSH logs are append-only** (`create` + `append` only). - **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-operation` records are counted, not imported. - **Wizard writes are approval-gated** โ€” anything but `allowed-once` means zero writes. ## Known limitations - Titles come from `custom-title`/`ai-title`/first prompt; Claude `summary` records are reported but not mapped to DSH compaction nodes (synthesizing a valid compaction transaction would fabricate its seq range and checkpoint message). - `thinking` blocks are kept as `reasoning` content 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.streamText` surface, transcripts larger than `maxTranscriptBytes` fail 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: false` plus a `reason`). The default `claudecode` workspace 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`; use `force: true` for 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. ## Model Experience - The model-facing surface is the two tools' descriptions/schemas and their outputs: `claude_scan` returns the structured index, `import_claude` returns per-file summaries with positions of warnings. Tool results are themselves logged `tool/result` events, so everything is reconstructable. - No hidden model-facing text; memory/`CLAUDE.md` sections are registered on `ctx.systemPrompt` (prompt assembly, rebuildable from the session log). ## Troubleshooting - Row not effective: `dsh --profile

--dump-config` should print `# == dsh-claude-move`; re-run `dsh plugin --profile

add ...`. - Web boots but hangs silently: new profiles initialized by `dsh plugin add` contain only `dsh-base` โ€” add `@deepseek-ai/dsh-web-app` to `dsh.profile.bundles`. Installing into the existing `web` profile needs nothing. - Panel routes 404: they are served only when `enableWebPanel: true` and a web server is composed; check the boot log for FAILED fibers. - Import fails with "transcript ่ฟ‡ๅคง": raise `maxTranscriptBytes` or 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 `dsh` console; the plugin logs `[claude-move]`-prefixed errors for workspace/import-map issues. ## Attribution (open-source components) 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](THIRD_PARTY_NOTICES.md)): - Conversion core vendored from [Nwflower/dsh-chat-import](https://github.com/Nwflower/dsh-chat-import) (MIT). - Discovery conventions & safety model from [Demogorgon314/dsh-resume-plugin](https://github.com/Demogorgon314/dsh-resume-plugin) (MIT). - Memory/skills injection & frontmatter parsing patterns from [YYTbit/dsh-plugin-claude-bridge](https://github.com/YYTbit/dsh-plugin-claude-bridge) (MIT). ## Development ```sh npm install # peer deps: @deepseek-ai/dsh-tools@0.1.0-rc.6, @deepseek-ai/cordis, schemastery npm test # node --test test/*.test.mjs ``` CI runs the full suite on Node 22 across Linux/macOS/Windows via GitHub Actions ([test.yml](.github/workflows/test.yml)). ## Topics `deepseek-harness`, `dsh-plugin`, `claude-code`, `migration`, `session-import`, `resume` ## Contributors - [@PerryLink](https://github.com/PerryLink) โ€” creator and maintainer: the import pipeline, the four-source migration wizard, the Web panel, docs, CI/CD and releases. - [@OLDnana1](https://github.com/OLDnana1) โ€” root-cause analysis of the interrupted tool-call corruption that made imported sessions permanently return HTTP 400 on resume. - [@GooodWei](https://github.com/GooodWei) โ€” identified `README.md` (and any description-less `.md`) being misregistered as a skill, which broke DSH's skill load. ## PerryLink DSH Plugin Family This project is one of the DeepSeek Harness plugins maintained by [PerryLink](https://github.com/PerryLink). If this one helps you, the others likely will too: | Plugin | One-liner | |---|---| | [dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel) | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | | [dsh-doublecheck](https://github.com/PerryLink/dsh-doublecheck) | Engineering-discipline guard: requirements grill, test gates, adversary review | | [dsh-background-agents](https://github.com/PerryLink/dsh-background-agents) | Durable background child agents with a Web UI sidebar, messaging and interrupt | | [dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions) | LSP diagnostics, formatting, completion, code actions and rename over language servers | | [dsh-output-styles](https://github.com/PerryLink/dsh-output-styles) | Claude Code outputStyles-equivalent runtime style switching | | [dsh-checkpoint-rewind](https://github.com/PerryLink/dsh-checkpoint-rewind) | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore | | [dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules) | Claude Code-style declarative allow/deny/ask permission rules with audit | | [dsh-auto-review](https://github.com/PerryLink/dsh-auto-review) | Second-model auto-review on the approval chain, fail-closed by default | | [dsh-memento](https://github.com/PerryLink/dsh-memento) | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | | [dsh-skill-pack-security](https://github.com/PerryLink/dsh-skill-pack-security) | Security-audit skill pack: secret scan, dependency and supply-chain review | | [dsh-session-pin](https://github.com/PerryLink/dsh-session-pin) | Pin sessions in the Web sidebar with durable ordering | | [dsh-composer-history](https://github.com/PerryLink/dsh-composer-history) | Terminal-style input history for the web composer: arrows, Ctrl+R search | | [dsh-github](https://github.com/PerryLink/dsh-github) | GitHub PR/issues integration for DSH, every write gated by approval | | [dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide) | Plugin-development knowledge base as an on-demand agent skill | | **[dsh-claude-move](https://github.com/PerryLink/dsh-claude-move)** | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH | ## License [Apache License 2.0](LICENSE) ยฉ 2026 dsh-claude-move contributors