An opencode plugin that adds a /move-session slash command for moving the current chat session to another project directory. Everything runs directly inside the plugin process — the LLM is never involved. Works on Windows, Linux, and macOS.
/move-session <target>
/move-session --debug <target>
<target> can be written as:
- An absolute path:
E:\repo\subdiror/Users/me/code/repo - A home-relative path:
~/Projects/foo - A relative path:
../sibling - The name of a known project — just its directory name, case-insensitive, e.g.
myrepo - Fixed phrases:
here/current dir/this folder/current directory— the current directoryhome/my home— your home directory- any word containing
projects,code,src, ordev— resolves to the first existing~/Projects,~/projects,~/code,~/src,~/dev
The phrases are a fixed list, not natural-language understanding; inputs containing those words are first treated as ordinary paths.
opencode binds every session to a project (recorded in the project_id, directory, and path columns). Once a session exists there is no built-in way to relocate it — the session list at the target directory never shows it, because those columns still point at the old location.
This plugin does the whole move inside the command.execute.before hook:
- Resolve the target path (plain code, deterministic, no model needed).
- Rewrite
project_id,directory,path, andworkspace_idfor the root session and all of its descendant subagent sessions in a single transaction. - If the target is a git repository, determine its project identity using opencode's own precedence: an existing project row with the same worktree → sha1 of the normalized origin remote → the
.git/opencodecache → the root commit hash, registering a new project row when necessary. Moving across repositories downgrades toglobalrather than hard-binding the session to an unrelated repo; the same downgrade happens on machines without git installed, with a warn log explaining why. - Report the result as a toast (success / already there / the verbatim error); on success, inject a synthetic
noReplysystem-reminder so the next turn knows the working directory changed. Finally throw to abort the command pipeline — no user message is ever recorded and the LLM is never called.
All you see is a brief toast confirmation; no /move-session trace remains in the conversation history, and no subagent sessions are created that would need cleaning up afterwards.
| File | Purpose |
|---|---|
plugins/move-session.js |
All of the logic: path resolution, git detection, writing opencode.db directly via bun:sqlite, toast and reminder injection |
commands/move-session.md |
A stub slash command (its template is just $ARGUMENTS) whose only job is to register /move-session; the plugin replaces its content entirely |
On Windows:
.\install.ps1On Linux and macOS:
./install.sh # set OPENCODE_CONFIG_DIR to use a different config directoryThe script simply copies the two files above into ~/.config/opencode/. If files from the original opencode-move-session or its PowerShell fork are detected, it prints a warning. Once you've confirmed the old version can go, re-run the script with -RemoveLegacy or --remove-legacy, or delete the old files yourself. Restart opencode afterwards.
Manual installation is just copying two files from this repository into opencode's config directory:
plugins/move-session.js→~/.config/opencode/plugins/(%USERPROFILE%\.config\opencode\plugins\on Windows)commands/move-session.md→~/.config/opencode/commands/(%USERPROFILE%\.config\opencode\commands\on Windows)
If you previously installed the older versions (session-env.js, move-session-auto-undo.js, or their command files), remove those too, otherwise the duplicate /move-session commands will conflict.
- The session ID comes straight from the hook arguments — no environment-variable injection needed. The database is read and written in-process via
bun:sqlite(with abusy_timeout), and git detection shells out throughBun.spawn(degrading gracefully when git is missing instead of failing). session.pathfollows the official formula (path.relative(project.directory, dir)); global sessions on Windows are stored without the drive letter, e.g.test/testfolder2.- Rewriting MSYS-style drive prefixes like
/c/Usersonly happens on Windows; POSIX paths are left untouched. --debugdiagnostics go to the opencode log (service namemove-session), never into any prompt text.
The idea comes from opencode-move-session (the original bash version) and its PowerShell fork: the move rules and path semantics are identical, but "prompt-driven LLM running a script" has been replaced by direct execution inside the plugin, so there is no dependency on sqlite3 or PowerShell, and no subagent sessions or pending-cleanup files to worry about. The reasons for dropping the LLM call: it can cost money; sometimes there is no usable model at hand; the LLM may not follow exactly what the skill says or may misunderstand it, causing all sorts of problems; without LLM randomness the behavior is deterministic and much easier to debug; and execution is faster. The trade-off, of course, is losing the LLM's ability to "intelligently" locate the target path.
- This plugin modifies
opencode.dbdirectly. If opencode changes the session/project schema or filtering rules, this needs updating accordingly. - Already-running opencode instances may keep the old project assignment in memory; switch sessions or restart to see the new location.
- Requires opencode to support the
command.execute.beforehook. - Without a TUI attached (headless or
opencode run) you won't see the toast, but the move still completes and the result is written to the log. - Throwing to abort the pipeline surfaces server-side as a failed
session.commandrequest — that's expected. - Target paths are not symlink-resolved; if your project paths contain symlinks, worktree matching may be affected.
Delete these two files and restart opencode:
~/.config/opencode/plugins/move-session.js
~/.config/opencode/commands/move-session.md