Hand off work between Claude Code and Codex without re-explaining
The next session begins without the last conversation. In Claude Code, Codex, OpenCode, Gemini CLI, Qwen Code, Cursor and GitHub Copilot CLI, a session is a saved conversation tied to a working directory or workspace. The expensive failure is not a crash. It is the next session starting from scratch: it re-reads the same files, tries the fix that already failed, and asks you what you decided yesterday. A handoff is a short written baton that prevents that: the smallest set of facts the next agent needs to take the next action safely.
Checked against each tool's documentation on 26–27 October 2026.
What goes in a handoff#
These fields, in this order:
# Handoff: retry the Stripe webhook fix
Updated: 2026-10-26 14:10 UTC
Repo / branch: billing-api / fix/webhook-idem (commit 8c1d2e4)
Goal: make duplicate webhook deliveries idempotent.
State: handler and migration 0042 are written; nothing is applied on staging.
Decisions: store Stripe event_id in `webhook_events`, not an in-memory LRU,
because two workers run behind the load balancer.
Failed: retrying by `created` timestamp. Stripe sends the same event with a
new delivery time, so the second delivery looked new. Test:
`pnpm test webhook.idempotency` — fails on "duplicate delivery".
Open: migration 0042 is untested against the staging replica.
Next: apply 0042 on staging, replay `evt_...`, check one row in `webhook_events`.
Files: src/billing/webhook.ts, prisma/migrations/0042_webhook_events.sql
Do not: raise the retry count; that is covered by the incident review.- Goal: the outcome, not the activity. "Make duplicate deliveries idempotent", not "work on the webhook".
- State: what is done, what is uncommitted, the commit to start from.
- Decisions: each with the reason.
- Failed: the attempt, the exact command and the error. Otherwise the next agent tries it again.
- Open: blockers only a person can answer.
- Next: one action and its command.
- Files: paths.
- Do not: constraints easy to violate, such as a frozen package or a secret.
Where to put it#
A file in the repository. docs/handoff.md is the simplest. Commit it if the next person needs it, or add it to .gitignore if it is scratch. A file needs a pointer.
A pointer in the file the agent already reads. Put one line in CLAUDE.md for Claude Code, in AGENTS.md for Codex, in AGENTS.md for OpenCode, and in GEMINI.md for Gemini CLI: Read docs/handoff.md before you change anything. Claude Code's memory docs load CLAUDE.md and read AGENTS.md only when no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md is present, unless you change the setting. Codex's AGENTS.md guide builds the chain from the global AGENTS.md under CODEX_HOME (default ~/.codex/AGENTS.md) and the repository root down to the working directory. Codex does not read CLAUDE.md. OpenCode V2 reads AGENTS.md only; V1 also reads CLAUDE.md when no AGENTS.md is on the path. Gemini CLI reads GEMINI.md by default and reads AGENTS.md only when you add it to context.fileName, so put the pointer in GEMINI.md unless you have configured the other name. Qwen Code reads both QWEN.md and AGENTS.md by default, so put the pointer in either; it does not read CLAUDE.md or GEMINI.md unless you add them to context.fileName, and that list replaces the defaults, so include every name you want. Cursor reads AGENTS.md and CLAUDE.md at the workspace root, so put the pointer in either; the Cursor AGENTS.md guide covers nested files. If the repository has both CLAUDE.md and AGENTS.md, keep the pointer in both. For Claude Code, you can also put @AGENTS.md in CLAUDE.md; on OpenCode, use the pointer in the instruction file that line reads, or list the handoff file in instructions.
A session-start hook. A command Claude Code runs by itself at session start, so the handoff reaches the session without the agent deciding to open it. In Claude Code, a SessionStart hook with the matcher startup|resume|clear|compact|fork adds its stdout to the context:
{"hooks":{"SessionStart":[{"matcher":"startup|resume|clear|compact|fork","hooks":[{"type":"command","command":"cat docs/handoff.md"}]}]}}Codex has the same event; in the CLI, trust the hook in /hooks before it runs. Gemini CLI also has a SessionStart hook; it can add hookSpecificOutput.additionalContext in its JSON output on startup, resume, or clear, and it has no compaction source at v0.65.0. Qwen Code has the same event, with startup, resume, clear, and compact sources; in settings.json, the command's stdout is added to context. The checklist below has the Qwen snippet. OpenCode has no session-start hook in its config. Add the handoff path to the instructions array if you want it loaded without a pointer; V2 reloads the current instruction files after compaction.
Handing off to a new Claude Code session#
You can reopen a saved session, or start a new one with a handoff.
Resume or start fresh? Resume when the next task is the same thread and the saved conversation still has the detail. claude --continue reopens the most recent session in the current directory; claude --resume opens a picker. Start fresh when the task changed, the old conversation was compacted, or you need a context that is easy to audit. Compaction replaces older turns with a model-written summary when the context window — the working text the model can see at once — fills, and the summary may have dropped the detail you need.
What to write. Use the fields above, plus the branch and commit, the uncommitted files, and the last command you ran.
Where to put it. docs/handoff.md in the repository. Put one line in CLAUDE.md so the new session reads it: Read docs/handoff.md before you change anything. If the file is in the repository, commit it, or give the new session the absolute path.
Inject it at session start. In .claude/settings.json, a SessionStart hook with the matcher startup runs the command and adds its stdout to the context:
{"hooks":{"SessionStart":[{"matcher":"startup","hooks":[{"type":"command","command":"cat docs/handoff.md"}]}]}}Add resume to inject the file after a resume, and fork for a forked session. The first response waits for the hook.
Across machines. Auto memory — Claude Code's own notes, stored on that machine — is machine-local, and transcripts are the saved conversation files under ~/.claude/projects on the machine that created them. There is no documented session sync. A session ID from another machine will not resolve. /export writes the conversation as plain text; carry that and the handoff file in the repository, or a shared drive, and start a fresh session on the other machine.
Find the session you want#
claude --continue reopens the most recent session in the current directory; claude --resume opens the session picker, an interactive list, and claude --resume <id-or-name> resumes directly. For picker scope, resume by ID, transcript retention and /export, see Resume the Claude Code session you want.
Claude Code to Codex, step by step#
- Write the file while the facts are in front of you. Ask Claude Code: "Write
docs/handoff.mdusing the template. Include the exact failing command and its error. Do not paraphrase." - Check the evidence, not the summary. Run
git status,git diff --stat,git log -1and the failing test. A handoff that says "tests pass" when one fails is worse than none. - Put the pointer in
AGENTS.md. Codex reads the chain from the repository root. Commit both, or give Codex the absolute path. - Start Codex in the repository. Ask: "Read
docs/handoff.md. Restate the goal, the decisions, the failed attempt, the open items and the next action. Do not edit yet." - Compare the restatement with the file. If a line is missing, paste it back. Do not accept "I have the context."
- After the work, update or retire the handoff. Rename it
handoff-done-2026-10-26.md, or write the next baton.
Codex to Claude Code, step by step#
- Write the same file. Codex can write
docs/handoff.mdand updateAGENTS.mdin one turn. - Add the pointer where Claude Code looks. If the repository has a
CLAUDE.md, addRead docs/handoff.md before you change anything.If it does not, Claude Code readsAGENTS.mdon v2.1.277 or later. You can also put@AGENTS.mdat the top ofCLAUDE.md. - Start Claude Code in the repository. Run
/contextand check that the instruction file appears under Memory files. - Ask for the restatement. The same five items, then the file and line it read.
- Check the first action. If it is a command, run it in dry-run mode if one exists. The handoff is a claim; the command is the test.
Importing a Claude Code setup: checklist#
Codex imports from Claude Code through /import in the CLI or Settings > Import in the desktop app. It does not change your existing setup. The CLI takes up to 50 chats from the last 30 days and is unavailable during a running task, in a remote session, or while connected to a local app-server daemon. The desktop app imports directly and can keep imported work in sync.
Status, 26 October 2026: two desktop-app reports matter for imported work. Imported chats can open blank while the transcript is still on disk (#20493, last updated 2 September 2026), and a large imported Claude Code session that was compacted can fail on the first follow-up because the import treats the old summary as ordinary messages and exceeds the API's input item limit (#34656, last updated 15 October 2026). In both cases, start a fresh thread with the handoff file.
The import page lists the destinations: instruction files to AGENTS.md, settings.json to config.toml, skills and plugins, project folders, project memories, chats from the last 30 days, MCP configuration, hooks, slash commands to skills, and subagents. The destination is documented; the behaviour is not the same.
What to check, because the destination is not the behaviour:
- Nested instruction files. Codex reads one file per directory from the project root to the working directory, and only on that path. A nested CLAUDE.md elsewhere does not load, even if its text ended up in an AGENTS.md outside that path. Move the rule onto the path, or into a skill if it is a procedure.
- The 32 KiB budget. The docs disagree on whether the limit is per file or for the project files as a group. The filed reports (#36371, #37956; both open on 26 October 2026) say one shared budget, consumed root-first, so the file nearest the working directory is cut when it runs out; the later report says the global file is outside that budget. Measure the chain, raise
project_doc_max_bytes, or split before you trust it. - Hooks and permissions. Hooks arrive, but their behaviour and tool coverage can differ. In the CLI, review them in
/hooks, trust them, and test one event. A Claude Code allow rule is not a Codex sandbox (what files and network the agent can touch) or approval policy (when it must ask); readapproval_policyandsandbox_modein the configuration reference, then run one read-only task. - Memory scope. The import sends Claude Code project memories to Codex memories. The store is one per Codex home, not one per repository, so a memory from one repository can surface in another. Keep a fact that must stay put in that project's
AGENTS.md. - Command-style prompts. Slash commands become skills; argument interpolation, shell interpolation and file-path placeholders may not carry over.
- MCP auth. MCP is the protocol Codex uses to call external tools; its authentication is separate from the import. Run
/mcpin the app or CLI, orcodex mcp listin the CLI, then open a tool. Configuration can arrive without a working sign-in.
Then run one real task end to end before you retire Claude Code. For the detail behind each check, see why Codex ignores part of your AGENTS.md, Codex hooks that don't fire, why Codex still asks for approval, and keep agent memory in the right project.
Importing a Claude Code setup into OpenCode: checklist#
OpenCode has no documented importer for a Claude Code setup. The CLI can import a saved OpenCode session, not a Claude Code configuration. Copy the parts that have an OpenCode equivalent, and leave the rest.
Status, 27 October 2026: V2 dropped the CLAUDE.md fallback, changed the permission, MCP and plugin shapes, and does not run V1 plugin code. A setup that worked on V1 can need more than a version bump.
- Rules. Move the text into
AGENTS.mdat the repository root. V2 readsAGENTS.mdonly; V1 readsCLAUDE.mdonly when noAGENTS.mdis on the path. Keep personal rules in the global file under the OpenCode config directory;opencode debug pathsprints it. If a rule is in a nested file, put it on a path the agent reads. V2 loads a nestedAGENTS.mdwhen the agent reads under it, not at startup. - Skills.
.claude/skillsand~/.claude/skillsare read by both V1 and V2, and so are.agents/skillsand~/.agents/skills. No copy is needed. If a skill is missing, check the environment flags that disable the compatibility paths and the skill's description. - Commands.
.claude/commandsis not on OpenCode's discovery list. Move each file to.opencode/commands/<name>.md(or~/.config/opencode/commands/).$ARGUMENTSand$1work; rewrite the frontmatter to OpenCode's fields (description,agent,model, andsubtaskin V1 orsubagentin V2). Claude Code's frontmatter is not translated. - Hooks and plugins. A Claude Code hook is not a config entry OpenCode reads. The closest is an OpenCode plugin, a JavaScript or TypeScript module that registers tool hooks or event handlers. V1 plugin implementations do not run in V2; port them to the V2 plugin API and test one hook.
- MCP.
.mcp.jsonis not read. Add each server undermcpin V1 ormcp.serversin V2 inopencode.json;opencode mcp addcan write the entry. Then runopencode mcp list(and/mcpsin V2), and check that the agent can call one tool. Authentication is separate from configuration. - Permissions and settings.
settings.jsonis not read. Translate the rules topermissionin V1 orpermissionsin V2. V2 uses the actionsshell,subagentandedit, and it allowsexternal_directoryby default; V1 asks. Run one harmless command that should be denied or asked. - Subagents.
.claude/agentsis not on OpenCode's discovery list. Recreate each one in.opencode/agents/or~/.config/opencode/agents/. V2 agent frontmatter usesdescription,mode,modelandpermissions; V1 usespermission. A copied Claude Code file is not a safe import: itstoolsand other Claude-specific fields are not translated, so it can load with the wrong permissions or be skipped. Recreate it with OpenCode fields and test one. - Test one real task. Start OpenCode in the repository, ask it to quote a line from
AGENTS.mdand name the file, then run a command that should be blocked. Do not retire Claude Code until that passes.
For the rest of the setup, see OpenCode's instruction loading and the OpenCode guides.
Importing a Claude Code setup into Copilot CLI: checklist#
Copilot CLI reads several Claude Code files directly, but it does not import a setup as a unit. It reads named files and a subset of .claude/settings.json; it does not convert the rest of the setup for you. Copy what has an equivalent, check each item in a live session, and keep Claude Code until one real task passes.
Run copilot --version before you rely on a version-specific item. The compatibility paths changed across 0.0.399 (single-file commands in .claude/commands/), 0.0.401 (Claude-style .mcp.json), 1.0.6 (Claude hook shape), 1.0.12 (.claude/settings.json), 1.0.36 (user-level ~/.claude agents, skills and commands stopped loading), 1.0.62 (nested .claude/agents) and 1.0.89 (.claude/rules). The CLI was 1.0.104 on 27 October 2026.
- Rules. Keep
CLAUDE.md: Copilot CLI reads it in the standard locations, including.claude/CLAUDE.md. Claude Code rule files under.claude/rulesare read as custom instructions since 1.0.89 (changelog).AGENTS.mdis read too; Claude Code uses it only when noCLAUDE.md,.claude/CLAUDE.mdorCLAUDE.local.mdis present (v2.1.277 or later). Personal rules belong in~/.copilot/copilot-instructions.mdor~/.copilot/instructions/**/*.instructions.md; the custom-instructions page does not list~/.claude/CLAUDE.mdas a user-level source. Put the handoff pointer inAGENTS.md,CLAUDE.md, or.github/copilot-instructions.md; all three are read. Copilot CLI combines all applicable files and does not define a general precedence order, so put a rule in one file rather than assuming the repository copy wins.
- Skills. Project
.claude/skillsand.agents/skillsare read; personal skills belong in~/.copilot/skillsor~/.agents/skills(skills docs).~/.claude/skillsis no longer loaded as user config since 1.0.36 (changelog). Check/skills list, then/skills info NAMEfor the location./skills reloadpicks up a skill added during a session.
- Commands. Single-file commands in
.claude/commands/are supported since 0.0.399 (changelog). Personal commands in~/.claude/commands/are not loaded since 1.0.36; move them into the project's.claude/commands/or rewrite them as skills. Check that each one runs. If one does not, rewrite the procedure as a skill; the skill's name and description are what make it discoverable.
- Agents. Custom agents in a project
.claude/agentsdirectory are discovered, including nested ones since 1.0.62 (changelog). Personal agents in~/.claude/agentsare not loaded since 1.0.36; recreate them in~/.copilot/agents/(custom agents docs). A subagent does not follow repository instructions unless its frontmatter setsinclude-custom-instructions: true. Check/agentand run the agent on one file.
- Hooks and plugins. The CLI reads
hooks,enabledPlugins,extraKnownMarketplaces,companyAnnouncementsanddisableAllHooksfrom.claude/settings.jsonand.claude/settings.local.json(config reference). It accepts Claude's nested matcher/hooks shape and PascalCase event names since 1.0.6 (changelog). A hook that assumes a POSIX shell can fail on Windows; repository hooks use.github/hooks/*.jsonwith separatebashandpowershellcommands (hooks docs). Hook configuration loads when the CLI starts, so restart and test one event. For a plugin, the settings keys are read, but test its commands, agents, hooks and MCP servers separately.
- Permissions. The docs' cross-tool list names hooks, plugin keys and announcements, not permission rules (config reference). Recreate allow rules as CLI flags (
--allow-tool,--allow-url) or saved approvals in~/.copilot/permissions-config.json; keep deny rules in--deny-tooland--deny-url, because the saved file holds approvals, not deny rules. Check/permissions showin the repository, then run one command that should ask and one that should be denied.
- MCP. A Claude-style
.mcp.jsonis read without themcpServerswrapper since 0.0.401 (changelog). Copilot CLI walks from the working directory to the repository root loading.mcp.jsonand.github/mcp.json; project definitions beat user definitions (MCP docs). Authentication is separate. After the config loads, run/mcp list,/mcp show NAME, and ask the agent to call one tool. A server can be connected and still contribute no visible tool if itstoolslist filters them; a disabled server contributes none by design.
- Settings. Do not copy
settings.jsonas a file. The CLI reads only the cross-tool subset from.claude/settings.json; its own settings live in~/.copilot/settings.json,.github/copilot/settings.jsonand.github/copilot/settings.local.json./settingsedits them, andcopilot configdoes the same from the shell (config reference). Copy the keys you can name, not the file.
- Test one real task. Start Copilot CLI in the repository. Run
/instructions,/env,/skills list,/agentand/mcp list. Ask it to quote a line from the rule file and name the path it came from; then run one command that should be allowed and one that should ask or be denied. Keep Claude Code until that passes. "I have the context" is not proof; the quote and the path are.
For the rest of the setup, see the Copilot CLI instructions guide, why your Copilot CLI hook or plugin isn't firing, and the Copilot CLI permissions guide.
Importing a Claude Code setup into Cursor: checklist#
Cursor reads several Claude Code files and directories directly — CLAUDE.md, .claude/skills and .claude/agents — and imports Claude hooks when the third-party setting is on. It does not read .claude/rules, and it does not convert the rest of the setup as a unit. Copy what has a Cursor equivalent, check each item in a live session, and keep Claude Code until one real task passes.
Run Cursor → About Cursor in the editor, or agent --version in the CLI. Cursor 3.27 was the newest editor build on 27 October 2026, and the CLI changelog listed v2026.10.22; current CLI docs call the command agent, and older posts say cursor-agent.
- Rules. Cursor reads a root
CLAUDE.mdas an always-applied file, and readsAGENTS.mdat the root and in subdirectories. In the editor, nested files attach when the agent reads a file in that folder; in the CLI since v2026.10.22, an edit or create also attaches them, so a package's rules may not load on the first turn. User-level rules belong in Customize → Rules; a~/.claude/CLAUDE.mdis not a documented Cursor source, and~/.cursor/rulesis version-dependent: the Help page describes it, staff said in June and August 2026 that it is unsupported (#158551, #170099), and a 26 October 2026 report on 3.24.12 says Customize lists files there (#174724). The Cursor AGENTS.md guide covers loading, and the rules guide covers the~/.cursor/rulescheck.
.claude/rules/ is not a supported project-rule location. With the third-party setting on, Cursor picks up CLAUDE.md and .claude/skills, not .claude/rules (a Cursor staff reply in #157087, 9 April 2026). Copy each file into .cursor/rules/ and rename it .mdc; staff confirmed there is no symlink or auto-sync (29 April 2026). Check Customize → Rules for the file and its type.
- Skills. Cursor reads
.claude/skills/and~/.claude/skills/for compatibility, plus.codex/skills/and~/.codex/skills/(skills reference). These compatibility roots depend on the third-party setting: staff said turning it off stops the.claude/,.codex/and.agents/roots (14 May 2026, #160677). A skill is a folder with aSKILL.md;namemust match the folder anddescriptionis required. Check Customize → Skills and the/menu. The catalog is fixed when a chat starts, so a skill added mid-session appears only in a new chat (staff, #165124, 8 July 2026). The Cursor skills guide covers the roots and the snapshot.
- Agents. Cursor reads
.claude/agents/and.codex/agents/, plus the user-level~/.claude/agents/and~/.codex/agents/..cursor/agents/wins when names conflict; project agents win over user agents (subagents reference). Customize → Subagents shows.cursor/agents/; imported files live in.claude/agents/or.codex/agents/, so check those paths or invoke one by name with a small task to prove it loaded. The Cursor skills guide has the subagent section.
- Commands. Cursor commands live in
.cursor/commands/*.md;.claude/commands/is not a documented source. Cursor 2.4's/migrate-to-skillsconverts eligible dynamic rules and slash commands into skills (skills help), which is the migration path that keeps the command invocable. Convert or recreate each one, then check the/menu.
- Hooks. Claude hooks load from
.claude/settings.local.json,.claude/settings.jsonand~/.claude/settings.jsonwhen Include Third-Party Plugins, Skills, and Other Configs is on under Settings → Agents → Third-Party Imports (third-party hooks). Cursor mapsPreToolUse,PostToolUse,Stop,SubagentStop,SessionStart,SessionEnd,PreCompactandUserPromptSubmit; it does not mapNotificationorPermissionRequest, and Claude'sGlobhas no Cursor equivalent. Test one event and watch the Hooks output channel. Imported plugin hooks can receive a Cursor-shaped payload and fail open (staff, 28 September 2026, #173171); on Windows, a hook can be built as PowerShell but run through bash (staff, 12 August 2026, #168129). The Cursor hooks guide has the checks.
Do not use the third-party toggle to hide CLAUDE.md: Cursor loads it even with the toggle off in a January 2026 report, and staff reproduced it on the first run after toggling (#149974, 26 January 2026).
- Permissions. Cursor's docs describe importing Claude Code hooks, skills and agents, not its permission rules. Recreate the boundary in the layer for the surface you use. The editor reads terminal and MCP allowlists from
~/.cursor/permissions.json(the per-user file; the project file's allowlists are the disputed part); the CLI uses~/.cursor/cli-config.jsonand.cursor/cli.jsonwithpermissions.allowandpermissions.deny(editor reference, CLI reference). The editor's file has no deny list, so use a hook or the CLI'spermissions.denyfor a hard block. Then run one command that should be denied in the CLI, or asked in the editor. The Cursor permissions guide covers which file is read.
- MCP. Cursor's documented config is
~/.cursor/mcp.jsonfor every project and.cursor/mcp.jsonfor one project (MCP reference); the docs do not list Claude's.mcp.jsonas a source. Re-add each server undermcpServers, then check Customize in the editor and/mcp listin the CLI, and call one tool. Authentication is separate. The Cursor MCP guide covers the connected-but-not-available cases.
- Chat history. Cursor does not document importing Claude Code conversations. Editor history is workspace-scoped, CLI history is separate, and a shared link is not a backup (chat history guide). Do not plan on moving the transcript; carry the handoff file and start a fresh chat.
- Test one real task. Start Cursor in the repository. Open Customize → Rules and look for the
AGENTS.mdorCLAUDE.mdentry; if it is not listed,@-mention the file. Open Customize → Skills; check MCP in Customize or with/mcp listin the CLI. Ask it to quote theNextline fromdocs/handoff.mdand name the path it came from; then run one command that should be denied in the CLI, or asked in the editor. If the rule is in a nestedAGENTS.md, ask it to read a file in that folder first. "I have the context" is not proof; the quote and the path are.
For the rest of the setup, see the Cursor guides, the Cursor AGENTS.md guide, and the Cursor rules guide.
Importing a Claude Code setup into Gemini CLI: checklist#
Gemini CLI has no general importer for a Claude Code setup; hooks are the exception (gemini hooks migrate --from-claude, item 5). It reads GEMINI.md by default and reads AGENTS.md only when you add it to context.fileName; it reads none of the .claude/ instruction, skill, or agent paths. Copy what has a Gemini equivalent, and check each item in a live session.
Run gemini --version before you rely on a version-specific item. The stable release on 27 October 2026 is v0.65.0.
- Rules. Put the text in
GEMINI.mdat the repository root, and personal rules in~/.gemini/GEMINI.md.AGENTS.mdis not read by default. To add it, setcontext.fileNamein~/.gemini/settings.jsonor.gemini/settings.json:
{
"context": {
"fileName": ["AGENTS.md", "GEMINI.md"]
}
}The setting adds names; at v0.65.0 it does not remove GEMINI.md, so a repository with both files sends both. Start a new session after the change. Check /memory list for the paths and /memory show for the text. If the folder is untrusted, repository context files are skipped.
- Skills.
.claude/skillsand~/.claude/skillsare not scanned. Put each skill folder with aSKILL.mdin.gemini/skills/,~/.gemini/skills/,.agents/skills/, or~/.agents/skills/. The scan is one level deep:.gemini/skills/my-skill/SKILL.mdworks; a deeper folder does not. Check/skills list all, orgemini skills list --allfrom the shell. A Claude Code skill withnameanddescriptionusually works: Gemini's loader takesnameanddescriptionfrom the frontmatter and uses the body as the skill text; extra frontmatter fields are ignored, and Claude-only tool names or substitutions in the body still will not work.
- Agents.
.claude/agentsand~/.claude/agentsare not read. Recreate each agent in.gemini/agents/or~/.gemini/agents/, one file per agent, flat. The schema rejects Claude fields such aspermissionMode,disallowedTools, andmaxTurns; use Gemini'smax_turnsand a YAML list of Gemini tool names (read_file,grep_search). Check/agents list; a repository agent also needs acknowledgement in the "New Agents Discovered" dialog before it can be called.
- Commands.
.claude/commands/*.mdis not read. Gemini custom commands are TOML files in.gemini/commands/or~/.gemini/commands/. Recreate the prompt as a TOML command and test its argument handling; the Claude frontmatter is not translated. Check/commands list.
- Hooks. Run
gemini hooks migrate --from-claudefrom the repository. It reads.claude/settings.local.jsonif present, otherwise.claude/settings.json, and writes the merged result to.gemini/settings.json. It mapsPreToolUsetoBeforeTool,PostToolUsetoAfterTool,UserPromptSubmittoBeforeAgent,StopandSubAgentStoptoAfterAgent, andPreCompacttoPreCompress; it also rewrites the Claude tool names in matchers and$CLAUDE_PROJECT_DIRto$GEMINI_PROJECT_DIR.
Check three things it does not fix: a lifecycle matcher is copied as a string, so a Claude SessionStart matcher of compact never matches; SubAgentStop now means the end of the main turn; and the timeout is copied unchanged, while Gemini's hook reference reads milliseconds. Check the number and convert it if your Claude hook used seconds. The command writes the merged hook set, including your user-level hooks, into .gemini/settings.json. Hook arrays concatenate across settings layers, so a user-level hook for a migrated event can run twice; remove the duplicate from the user settings after migrating. Open /hooks panel and run one log test.
- MCP.
.mcp.jsonis not read. Add each server undermcpServersin~/.gemini/settings.jsonor.gemini/settings.json;gemini mcp add <name> <command> --scope userwrites the user file, and the default is the repository file. A repository entry with the same name replaces the user entry as a whole. Authentication is separate: run/mcp auth <server>for an OAuth server, then/mcpto check the tools. Start a new session after adding or renaming a server.
- Permissions. Gemini CLI does not read Claude Code permission rules from
settings.json. Recreate a hard deny as a policy in~/.gemini/policies/*.tomlwithtoolName,commandPrefixorargsPattern,decision = "deny", and apriority. The workspace tier (.gemini/policies/) is documented as non-functional at v0.65.0, so put the rule in the user directory or an admin policy. Check/policies list, then run one command that should be denied.
- Settings and extensions. Do not copy
settings.jsonas a file. Gemini settings live in~/.gemini/settings.json,.gemini/settings.json, and the system files. Claude Code plugins and marketplaces do not become Gemini extensions; install Gemini extensions separately withgemini extensions install <source>and checkgemini extensions list. The settings keys are different, so copy the values you can name rather than the file.
- Test one real task. Start Gemini CLI in the repository. Run
/memory show,/skills list all,/agents list,/commands list,/hooks panel,/mcp, and/policies list. Ask it to quote theNextline fromdocs/handoff.mdand name the path it came from; then run one command that should be denied. Keep Claude Code until that passes.
For the rest of the setup, see Gemini CLI's AGENTS.md guide, the Gemini CLI permissions guide, and the Gemini CLI hooks guide. The Gemini CLI guides cover the other surfaces.
Importing a Claude Code or Gemini CLI setup into Qwen Code: checklist#
Qwen Code is a fork of Gemini CLI, so some Gemini names and paths have a Qwen counterpart. Neither move is a straight copy: Qwen reads neither GEMINI.md nor CLAUDE.md by default, and its settings live under .qwen, not .gemini or .claude. Copy what has a Qwen equivalent, check each item in a live session, and keep the old tool until one real task passes.
Run qwen --version before you rely on a version-specific item. The stable release on 27 October 2026 is v0.26.2.
- Rules. Qwen reads
QWEN.mdandAGENTS.mdby default, plus.qwen/QWEN.local.mdat the repository root. It does not readGEMINI.mdorCLAUDE.mdunless you add the name tocontext.fileName. That setting replaces the default list, so list every name you want:
{
"context": {
"fileName": ["QWEN.md", "AGENTS.md", "GEMINI.md"]
}
}Do not list a file and also a second file that imports it with @. A CLAUDE.md that contains @AGENTS.md will load AGENTS.md twice when both names are in the list (#15679, open). Check /context detail: the Memory files section names every context file that loaded.
- Skills. Qwen reads
~/.qwen/skills/,.qwen/skills/,~/.agents/skills/, and.agents/skills/. A Claude skill in.claude/skillsis not scanned; add that directory toskills.directoriesor copy the folder. A Gemini skill folder with aSKILL.mdcan be copied to.qwen/skills/or.agents/skills/. Check/skills.
- Agents. Qwen reads
.qwen/agents/(repository, highest precedence) and~/.qwen/agents/(user). It accepts the Claude Code frontmatter fieldspermissionMode,maxTurns,color,mcpServers, andhooks, so a Claude agent file can usually be dropped into.qwen/agents/. A Gemini agent file uses a different schema; rewrite it. Check/agents.
- Commands. Qwen reads Markdown command files from
.qwen/commands/and~/.qwen/commands/; TOML is deprecated but still read. A Gemini TOML command may work as-is because Qwen still reads TOML, but migrate it to Markdown and test it..claude/commands/is not on Qwen's list, so recreate each Claude command. Check/helpfor the command list.
- Hooks. Qwen uses Claude-style event names (
PreToolUse,PostToolUse,UserPromptSubmit,Stop,SessionStart,SessionEnd,PreCompact,PostCompact) and accepts Claude tool aliases such asBash,Read, andWritein matchers. Move thehooksobject into.qwen/settings.jsonor~/.qwen/settings.json. For a handoff, aSessionStarthook can put the file in context on startup and after a compaction:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{ "type": "command", "command": "cat \"$QWEN_PROJECT_DIR/docs/handoff.md\"" }
]
}
]
}
}The command's stdout is added to the model context on those sources. Run /hooks list and test one event. A Gemini hook uses different event names (BeforeTool, AfterTool, BeforeAgent, PreCompress) and must be remapped; do not paste a Gemini settings file into Qwen.
- MCP. Qwen reads
mcpServersfromsettings.json. Add each server under that key, or runqwen mcp add <name> <command>(user scope by default;--scope projectwrites.qwen/settings.json). Move a Claude or Gemini server entry into that block rather than leaving it in the old file. Authentication is separate: run/mcpto check the servers and ask the agent to call one tool.
- Permissions and settings. Qwen settings live in
~/.qwen/settings.json(user) and.qwen/settings.json(repository). Claude permission rules use a similar shape, but Qwen does not read them from.claude/settings.json; recreate them underpermissions.allow,permissions.ask, andpermissions.deny, wheredenywins. Gemini policies have no Qwen equivalent; put a hard block inpermissions.deny. Trust the folder first: an untrusted folder causes Qwen to skip repository settings and context files./permissionsopens the trust dialog.
- Extensions.
qwen extensions install <source>installs Gemini extensions and Claude marketplace plugins. Qwen convertsgemini-extension.jsonandclaude-plugin.jsontoqwen-extension.json, migrates Gemini TOML commands to Markdown, and converts Claude agents and skills. MCP servers, context files, and settings are preserved for Gemini extensions. Hooks are not in the documented conversion list, so check/hooksand test each capability./extensions listshows what installed.
- Authentication. Nothing carries over from either tool. Run
/authand pick a route. Qwen OAuth's free tier was discontinued on 2026-04-15; the current menu offers QwenCloud, Alibaba ModelStudio, third-party providers, and a custom provider.
- Test one real task. Start Qwen in the repository. Run
/memory,/context detail,/skills,/agents,/hooks list,/mcp, and/permissions. Ask it to quote theNextline fromdocs/handoff.mdand name the file it came from; then run one command that should be denied. Keep the old tool until that passes. "I have the context" is not proof; the quote and the path are.
For the rest of the setup, see Qwen Code instruction loading and the Qwen Code guides.
Using ai-memory for the handoff#
ai-memory has a handoff as a first-class object. An agent calls memory_handoff_begin with a summary, next steps and files. The next SessionStart prints it. In our test of v3.2.5, a handoff from a Claude Code-shaped session printed at the next Codex session start, then memory_handoff_list returned empty: claim-once.
Status, 26 October 2026: #959 is closed. The default still claims. Set [handoff] claim_on_session_start = false in config.toml to make SessionStart show a notice with the handoff id instead of consuming it; then accept it explicitly. Our v3.2.5 test used the default and did not exercise the setting. If your build lacks it, put the target in the summary or use a file for one specific task.
Without a handoff, the default startup packet in our test was empty; the agent had to call memory_briefing, or memory_query and memory_read_page.
Check that it arrived#
| Where the handoff lives | What to ask or run | What proves it |
|---|---|---|
| A file in the repo | "Quote the Next line." Then git log -1 --stat | The quote matches the commit. |
Claude Code SessionStart hook | "What did the hook print?" and /context | The output is in the first response. |
Codex SessionStart hook | "Quote the first line." and /hooks in the CLI | The hook is trusted and its output is in the session. |
OpenCode instructions | opencode debug config, then "Quote the Next line." | The entry is in the config and the quote matches the file. |
OpenCode AGENTS.md | V2 instruction record (the AGENTS page has the SQL) or a prompt log, then quote | The record or log shows the file and the quote matches. |
| Copilot CLI instruction file | /instructions, then "Quote the Next line." | The file is listed and the quote matches the file. |
Cursor AGENTS.md / CLAUDE.md | Customize → Rules, then "Quote the Next line." If the file is not listed, mention it in a new chat (@AGENTS.md or @CLAUDE.md, whichever holds the pointer) | The entry is listed with the always-applied rules (the docs do not promise a type label), or the mention returns the text, and the quote matches the file. |
Gemini CLI GEMINI.md | /memory list, /memory show, then "Quote the Next line." | The path is listed and the quote matches the file. |
Qwen Code QWEN.md / AGENTS.md | /context detail, then "Quote the Next line." | Memory files lists the file, and the quote matches. |
| ai-memory handoff | The first response after SessionStart; then memory_handoff_list | The text appears once, and the list is empty because it was consumed. |
The only proof is the next agent quoting the line or pointing at the file. "I have the context" is not proof. A model can say that after reading nothing.
What makes a handoff fail#
- No next action. A status report says where you are; a handoff says where to go.
- The failure is hidden. "Tests fail" is not enough. Paste the command and the error.
- No date or commit. The next agent cannot tell whether the handoff is current.
- Too long. The important line is buried, and the agent reads the first screen.
- It exists only in chat.
/compact(or/compressin Qwen Code) replaces older turns with a summary; Cursor's/summarizecompresses the whole conversation and keeps no tail, and a new session never sees the chat. The next tool cannot see it either. - Two copies disagree. A
CLAUDE.mdrule and an auto-memory note can produce an arbitrary answer.
This guide is part of Persistent memory for Claude Code and Codex.