
Security News
vlt 1.5.0 Adds Agent Skills Management
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.
@nano-step/opencode-worktree-plugin
Advanced tools
Manage git worktrees across parallel OpenCode sessions — plugin + CLI
Manage git worktrees across parallel OpenCode sessions. Create named worktrees, open them in new terminals, view their status, and commit — all without switching branches or losing uncommitted work.
feature/WIN-123)owt <name> opens a worktree in a new terminal windowowt diff, owt status, owt log, owt merge, owt commit without cdowt hook removes AI-injected Co-authored-by: from every commitnode_modules — symlinked from main repo; no duplicate installs/init-feature WIN-123 creates worktrees for multiple repos under one ticket folder; AI gets full feature context injected automatically.opencode/worktrees/) or globally (~/.config/opencode/worktrees/) — configured per project@latest (v4.x) — current stable. Includes feature-first cross-repo workflows, local state mode, and all slash commands.Previous stable: v3.2.0 (zero-project-footprint state). v4 is backward compatible — existing worktrees keep working.
OWT has three layers that work together. Each layer can be used independently.
┌─────────────────────────────────────────────────────────────┐
│ You (human) │
├──────────────┬──────────────────────┬───────────────────────┤
│ OpenCode │ OpenCode session │ Terminal │
│ main session│ (worktree) │ │
│ │ │ │
│ /init-worktree ──→ createworktree ──┼──→ owt <name> │
│ /list-worktrees ─→ listworktrees │ owt status/diff/ │
│ /open-worktree │ log/merge/commit │
├──────────────┴──────────────────────┴───────────────────────┤
│ Slash Commands (command/*.md) │
│ Parse arguments, call plugin tools │
├─────────────────────────────────────────────────────────────┤
│ Plugin (worktree-plugin.js) │
│ 3 tools: createworktree / deleteworktree / │
│ listworktrees │
│ + system prompt injection │
│ + state management (worktree-sessions.json) │
├─────────────────────────────────────────────────────────────┤
│ Shell Script (owt.sh) │
│ Terminal manager: list, open, status, diff, │
│ log, merge, commit, hook │
├─────────────────────────────────────────────────────────────┤
│ git worktree (built-in git) │
│ The actual isolation mechanism │
└─────────────────────────────────────────────────────────────┘
worktree-plugin.js)The plugin registers three tools that OpenCode's AI agent can call:
| Tool | What it does |
|---|---|
createworktree | Runs git worktree add, symlinks node_modules, saves session state |
deleteworktree | Checks for uncommitted changes, runs git worktree remove, cleans up state |
listworktrees | Combines git worktree list with saved display names and dirty status |
createfeature | Creates a feature folder with worktrees for multiple repos in one command |
listfeatures | Lists all features with per-repo clean/dirty/missing status |
deletefeature | Aggregate dirty check across all repos, then removes all worktrees atomically |
System prompt injection: When OpenCode starts a session, the plugin injects context into the AI's system prompt:
deleteworktree when done, stop after cleanupcreateworktree is available for parallel taskslistworktreesThis is how the AI "knows" it's inside a worktree without being told.
State management: Display names, session IDs, and creation timestamps are stored in .opencode/worktree-sessions.json. This file is read by both the plugin and the shell script. It is not committed to git.
command/*.md)Markdown files that define /init-worktree, /list-worktrees, and /open-worktree. These are prompt templates that:
They are the user-facing interface inside OpenCode sessions. The AI reads the markdown instructions and executes them.
owt.sh)A standalone bash script for use outside OpenCode — in any terminal. It reads the same worktree-sessions.json state file to resolve display names, and calls git directly for all operations.
Key capabilities not available through the plugin:
opencode --cwdowt fix matches "Fix login bug" by partial name or branchowt diff, owt log, owt status without cd-ing into the worktreeCo-authored-by: lines from commit messages/init-worktree feature/WIN-123 name:"Fix login bug"
│
▼
Slash command parses args
│
▼
createworktree(branch="feature/WIN-123", name="Fix login bug")
│
├─ Branch exists locally? → git worktree add <path> <branch>
├─ Branch exists remote? → git fetch + git worktree add
└─ New branch? → git worktree add <path> -b <branch> <base>
│
▼
Symlink node_modules from main repo → worktree
│
▼
Save to .opencode/worktree-sessions.json:
{ "sessions": { "<sessionId>": { "branch": "...", "name": "...", "worktreePath": "..." } } }
│
▼
User opens: opencode --cwd "~/.config/opencode/worktrees/repos/<repo-id>/worktrees/feature/WIN-123"
(or .opencode/worktrees/feature/WIN-123 in local state mode)
│
▼
Plugin detects worktree directory → injects WORKTREE_RULES into system prompt
│
▼
AI agent operates in isolated branch, full source checkout
owt owt fix-login
│ │
▼ ▼
git worktree list Fuzzy-match "fix-login" against
+ display names and branch names
worktree-sessions.json │
│ ▼
▼ Resolve to single worktree path
Display all with │
✓/⚠ dirty status ▼
Detect terminal emulator
│
▼
Open new window:
opencode --cwd <worktree-path>
Two paths depending on whether you want the owt CLI permanently.
One command copies everything into ~/.config/opencode/:
# stable (v2.x)
npx @nano-step/opencode-worktree-plugin install
# beta — workspace mode (v3.0.0+)
npx @nano-step/opencode-worktree-plugin@beta install
This copies the slash commands into ~/.config/opencode/command/ and the plugin into ~/.config/opencode/plugins/. No package is left installed on your machine.
Then restart OpenCode. Plugins and commands are loaded at startup — nothing takes effect until you do.
Note:
npxruns the installer temporarily. TheowtCLI is not added to your PATH. To test the install worked, open OpenCode and run/init-worktree. If you wantowton your PATH permanently, use Path B instead.
owt CLI)# stable (v2.x)
npm install -g @nano-step/opencode-worktree-plugin
# beta — workspace mode (v3.0.0+)
npm install -g @nano-step/opencode-worktree-plugin@beta
owt-setup install
npm install -g puts owt and owt-setup on your PATH. owt-setup install copies the slash commands and plugin into ~/.config/opencode/ (same files as npx).
Then restart OpenCode.
Optionally, strip the AI co-author line from commits:
owt hook --global
Verify everything is working:
owt help
Troubleshooting: If
owt helpworks but OpenCode doesn't recognize/init-worktree, you haven't restarted OpenCode yet.
Available in
@beta(v3.0.0+). Install withnpx @nano-step/opencode-worktree-plugin@beta install.
If you open OpenCode at a folder containing multiple git repos (not itself a git repo), the plugin automatically detects and works with all of them.
~/projects/
├── frontend/ ← .git
├── backend/ ← .git
└── shared-lib/ ← .git
opencode --cwd ~/projects # opens at workspace level — plugin finds all 3 repos
/init-worktree feature/new-dashboard repo:"frontend" name:"Dashboard redesign"
/list-worktrees
Shows all repos, even those with no active worktrees.
.git foldersnode_modules are skipped~/.config/opencode/worktrees/repos/<repo-id>/worktrees/ by default (global state). Set stateLocation: "local" in .opencode/worktree-config.json to keep them inside the project instead.If you open OpenCode inside a git repo (the existing workflow), everything works exactly as before. The repo parameter is optional and ignored in single-repo mode.
Requires v4.0.0+. Works in workspace mode (ecosystem folder with multiple repos).
The standard /init-worktree creates one worktree per repo independently. Feature-first groups them: one ticket ID creates a folder containing worktrees for all involved repos, and the AI automatically knows which feature it's working on.
~/.config/opencode/worktrees/features/
└── win-123-dashboard-redesign/
├── feature.json ← feature metadata
├── frontend/ ← git worktree (branch: feature/WIN-123)
└── backend/ ← git worktree (branch: feature/WIN-123)
Each feature folder contains a feature.json file created by /init-feature. It stores the feature metadata and is used by the plugin to detect feature mode when you open a worktree session.
{
"version": 1,
"id": "WIN-123",
"slug": "win-123-dashboard-redesign",
"name": "Dashboard redesign",
"branch": "feature/WIN-123",
"ecosystemDir": "/Users/you/projects",
"createdAt": 1713820800000,
"repos": {
"frontend": {
"source": "/Users/you/projects/frontend",
"worktreePath": "frontend"
},
"backend": {
"source": "/Users/you/projects/backend",
"worktreePath": "backend"
}
}
}
Do not edit this file manually. It is managed by the plugin.
Open OpenCode at your ecosystem folder:
opencode --cwd ~/projects # must be a workspace (folder containing repos, not itself a git repo)
Then:
/init-feature ticket:WIN-123 name:"Dashboard redesign" branch:feature/WIN-123 repos:frontend,backend
Output:
✓ Feature "win-123-dashboard-redesign" created.
Branch: feature/WIN-123
Path : ~/.config/opencode/worktrees/features/win-123-dashboard-redesign
frontend: opencode --cwd "~/.config/opencode/worktrees/features/win-123-dashboard-redesign/frontend"
backend: opencode --cwd "~/.config/opencode/worktrees/features/win-123-dashboard-redesign/backend"
Open each repo in a separate terminal — each session is fully isolated on feature/WIN-123.
When you open a feature worktree session, the AI automatically receives:
FEATURE CONTEXT: WIN-123 — Dashboard redesign
You are working on the FRONTEND repo (1 of 2 in this feature).
Other repos in this feature:
- backend: opencode --cwd "..."
Branch: feature/WIN-123 (all repos)
No manual prompting needed — the AI knows which feature, which repo, which branch, and how to switch to sibling repos.
/list-features
⚠ WIN-123 — Dashboard redesign
Branch: feature/WIN-123
Repos : frontend [dirty], backend [clean]
Open : opencode --cwd "..."
/delete-feature ticket:WIN-123
Refuses if any repo has uncommitted changes. Removes all worktrees atomically. Never deletes branches.
Option A (npx, no permanent install):
npx @nano-step/opencode-worktree-plugin install
Downloads the package, copies config files into ~/.config/opencode/, then exits. Nothing stays on your PATH.
Option B (global, gives you owt CLI):
npm install -g @nano-step/opencode-worktree-plugin
owt-setup install
npm install -g puts owt and owt-setup on your PATH permanently. owt-setup install copies the slash commands into ~/.config/opencode/command/ and the plugin into ~/.config/opencode/plugins/. Requires Node.js >= 18.
Troubleshooting: If
owtis not found after global install, your npm global bin directory may not be in your PATH. Runnpm bin -gto find it.
Restart OpenCode. This is the most common setup gotcha. Plugins and slash commands are loaded once at startup, so changes don't take effect until you restart.
After restarting:
/init-worktree in OpenCode (works for both paths)owt help in your terminal (global install only)Per-project plugin loading (alternative to global config): Instead of copying to ~/.config/opencode/, you can load the plugin per project via opencode.json:
{
"plugin": ["@nano-step/opencode-worktree-plugin"]
}
OpenCode resolves the package from your project's node_modules. Useful when different projects need different plugin versions.
If you use OpenCode to commit, it injects Co-authored-by: Sisyphus into every commit message. To strip it globally:
owt hook --global
This installs a commit-msg git hook at ~/.config/git/hooks/commit-msg and sets git config --global core.hooksPath. One-time setup, applies to all repos on your machine.
Note:
git commit --no-verifybypasses hooks including this one.
If you previously installed the plugin by cloning the repo and copying files manually, remove the old copy before switching to the npm-based install:
rm ~/.config/opencode/plugins/worktree-plugin.js
If you also switch to opencode.json plugin loading (the "plugin": [...] key), make sure the file-based copy above is gone first. Otherwise the plugin loads twice, which causes duplicate tools and confusing behavior.
If you were using the unscoped opencode-worktree-plugin package, update your opencode.json to reference the scoped package:
{
"plugin": ["@nano-step/opencode-worktree-plugin"]
}
Then follow the Quick start steps above.
v4 is backward compatible. Existing worktrees continue to work without any manual steps.
On first load after upgrading, the plugin automatically reorganizes the global state directory:
Before (v3):
~/.config/opencode/worktrees/
├── <repo-id>/
│ ├── worktrees/
│ └── worktree-sessions.json
After (v4):
~/.config/opencode/worktrees/
├── repos/
│ └── <repo-id>/
│ ├── worktrees/
│ └── worktree-sessions.json
└── features/ ← new in v4
The migration runs once and creates a .migrated-v4 sentinel file to skip on subsequent loads. If migration fails (non-fatal), the plugin logs a warning and continues — your worktrees are not deleted.
owt
Output:
Active worktrees:
⚠ Fix login bug [dirty (uncommitted changes)]
Branch : feature/WIN-123
Path : /project/.opencode/worktrees/feature/WIN-123
Open : opencode --cwd "/project/.opencode/worktrees/feature/WIN-123"
✓ pod-3 hotfix [clean]
Branch : release/pod-3
Path : /project/.opencode/worktrees/release/pod-3
Open : opencode --cwd "/project/.opencode/worktrees/release/pod-3"
owt fix-login # partial match on name or branch
owt WIN-123 # matches "feature/WIN-123"
owt pod # matches "pod-3 hotfix"
Spawns a new terminal window running opencode --cwd <worktree-path>. Supports: Ghostty, iTerm, Terminal.app, WezTerm, Kitty, Alacritty, tmux.
owt status # all worktrees at once
owt status fix-login # specific worktree
owt diff fix-login # full diff vs base branch
owt diff fix-login -- src/auth.ts # specific file
owt log fix-login # commits since branched off base
owt merge fix-login # merge into current branch
owt merge fix-login --into main # merge into specific branch
owt merge fix-login --into release/qa3
Guards against dirty state — will error if uncommitted changes exist.
owt commit fix-login -m "feat: fix login redirect"
Runs git add -A + git commit inside the worktree. Useful when you want to commit without opening a new terminal.
Guideline: If you are on
mainand need to commit changes that belong to another worktree, always useowt commit <name> -m "..."instead of switching branches. This keeps each branch's history clean and avoids accidentally committing feature work ontomain.Examples:
owt commit implement-a -m "feat: implement A" # commits into feature/implement-A owt commit fix-login -m "fix: login redirect" # commits into feature/fix-login owt commit pod-3 -m "chore: bump version" # commits into release/pod-3
owtmatches by partial name or branch —implement-amatchesfeature/implement-A.
owt hook --global # all repos on this machine (recommended)
owt hook --local # current repo only
owt hook --worktree WIN-123 # specific worktree (note: shares .git/hooks with main)
Single-repo worktrees:
| Command | Description |
|---|---|
/init-worktree <branch> [name:"..."] | Create a named worktree |
/list-worktrees | List all worktrees with status |
/open-worktree [name-or-branch] | Print opencode --cwd command |
/owt-current | Show current worktree name/branch/status |
Feature-first (cross-repo):
| Command | Description |
|---|---|
/init-feature ticket:<id> name:"..." branch:<b> repos:<r1,r2> | Create feature folder with worktrees per repo |
/list-features | List all features with per-repo status |
/delete-feature ticket:<id> | Delete all worktrees for a feature |
Examples:
/init-worktree feature/WIN-123 name:"Fix login bug"
/init-worktree hotfix/crash name:"Critical crash" from:main
/list-worktrees
/open-worktree fix-login
/owt-current
/init-feature ticket:WIN-123 name:"Dashboard redesign" branch:feature/WIN-123 repos:frontend,backend
/list-features
/delete-feature ticket:WIN-123
/owt-current)Run /owt-current from any OpenCode session to see what worktree you are in.
Inside a worktree:
Current worktree:
Name : Fix login bug
Branch : feature/WIN-123
Path : ~/.config/opencode/worktrees/repos/.../worktrees/feature/WIN-123
Status : clean
Main repo: /Users/you/projects/myapp
Inside a feature worktree:
FEATURE CONTEXT: WIN-123 — Dashboard redesign
You are working on the FRONTEND repo (1 of 2 in this feature).
Other repos in this feature:
- backend: opencode --cwd "..."
Branch: feature/WIN-123 (all repos)
Path : ~/.config/opencode/worktrees/features/win-123-dashboard-redesign/frontend
Status : clean
In the main repo:
You are in the main repository.
Branch : main
Path : /Users/you/projects/myapp
Use /init-worktree to create a parallel worktree session.
These are the most common questions when using worktrees for the first time.
Yes. A worktree is not a symlink or a partial copy. It is a complete checkout of the branch. Every file in the project exists inside the worktree directory.
project/ ← full source, branch: main
~/.config/opencode/worktrees/repos/<repo-id>/
worktrees/feature/WIN-123/ ← full source, branch: feature/WIN-123
worktrees/release/pod-3/ ← full source, branch: release/pod-3
# Or with stateLocation: "local":
project/.opencode/worktrees/
feature/WIN-123/ ← full source, branch: feature/WIN-123
release/pod-3/ ← full source, branch: release/pod-3
The only thing that is symlinked is node_modules (to avoid duplicate installs).
No. Each session operates in its own directory. Session A edits worktrees/feature/WIN-123/src/Button.tsx. Session B edits worktrees/release/pod-3/src/Button.tsx. These are different files on disk.
Git also enforces branch locking — the same branch cannot be checked out in two worktrees simultaneously. If you try, git returns an error.
Yes, immediately. All worktrees share a single .git repository. A commit in worktree A goes into the branch of worktree A. From the main session you can run:
git log feature/WIN-123 --oneline # see the commit immediately
git diff main...feature/WIN-123 # diff without switching branches
You do not need to cd into the worktree to inspect its commits.
No. Uncommitted changes stay in their worktree directory indefinitely. There is nothing to stash. To "switch" to another worktree, just cd to it or open a new terminal. The changes in the previous worktree are untouched.
This is the main advantage over git checkout — you never need to stash.
The main repo directory is on a different branch (e.g. main or release/qa3). Committing there puts the commit on that branch, not on feature/implement-A.
To commit feature A, you must be in worktrees/feature/implement-A/ — or use owt commit implement-A -m "...".
npm test inside a worktree?Yes. Each worktree has the full project structure and node_modules symlinked from the main repo. Run npm test, npm run build, or npm run start directly inside the worktree directory.
To run multiple features simultaneously on different ports:
# Each worktree gets its own port — as many as you need
PORT=3001 npm run start # in worktrees/feature/implement-A
PORT=3002 npm run start # in worktrees/feature/implement-B
PORT=3003 npm run start # in worktrees/feature/implement-C
# ... and so on
You do not need to. From the main repo, use git -C to run git commands in any worktree without cd:
git -C .opencode/worktrees/feature/WIN-123 diff
git -C .opencode/worktrees/feature/WIN-123 log --oneline -5
git -C .opencode/worktrees/feature/WIN-123 status
Or use owt diff WIN-123, owt log WIN-123, owt status WIN-123.
In OpenCode (main session):
/init-worktree feature/implement-A name:"Feature A"
/init-worktree feature/implement-B name:"Feature B"
/init-worktree feature/implement-C name:"Feature C"
/init-worktree feature/implement-D name:"Feature D"
owt feature-a # opens terminal with opencode --cwd <path>
owt feature-b
owt feature-c
owt feature-d
Now you have 4 OpenCode sessions, each on their own branch, working in parallel.
owt # see which are dirty/clean
owt status # git status of all worktrees
owt log A # commits in feature A
Option A — commit inside the worktree session:
# Inside the feature-A session
git add .
git commit -m "feat: implement A"
Option B — commit from main session without cd:
owt commit feature-a -m "feat: implement A"
owt merge feature-a --into release/qa3
npm test # verify after merge A
owt merge feature-b --into release/qa3
npm test # verify after merge A+B
# ... repeat for C, D
In OpenCode, call deleteworktree for each worktree, or:
git worktree remove .opencode/worktrees/feature/implement-A
worktree-config.json)Per-project worktree behavior is controlled by .opencode/worktree-config.json in your project root. This file is optional — all settings have defaults.
The first time you run /init-worktree or /init-feature in a project, the plugin checks for .opencode/worktree-config.json. If the file does not exist, you will be prompted once:
No worktree config found. Where should worktrees be stored?
[1] Global — ~/.config/opencode/worktrees/ (default, shared across all projects)
[2] Local — .opencode/worktrees/ inside this project
Reply with 1 or 2.
The plugin creates .opencode/worktree-config.json with your choice and proceeds. You can change the setting at any time by editing the file directly.
// .opencode/worktree-config.json
{
"stateLocation": "global",
"worktreesRoot": null,
"repos": null
}
stateLocationControls where worktree state (sessions, worktree directories) is stored.
| Value | State file | Worktrees created at |
|---|---|---|
"global" (default) | ~/.config/opencode/worktrees/repos/<repo-id>/ | ~/.config/opencode/worktrees/repos/<repo-id>/worktrees/<branch> |
"local" | <project>/.opencode/ | <project>/.opencode/worktrees/<branch> |
Use "local" if you want worktrees to live inside your project directory (e.g., visible in the same filesystem tree, or when you don't want global state):
{ "stateLocation": "local" }
Worktrees then appear at <project>/.opencode/worktrees/<branch>. Add .opencode/worktrees/ to your .gitignore.
worktreesRootOverride where worktrees are physically created (relative to project root). Only applies when stateLocation is "local" or when you want a custom path.
{
"stateLocation": "local",
"worktreesRoot": ".worktrees"
}
Worktrees appear at <project>/.worktrees/<branch>.
repos (workspace mode only)Explicitly list which repos to include instead of auto-discovering all git repos in the folder:
{
"repos": {
"frontend": "./frontend",
"backend": "./backend"
}
}
Note:
stateLocation: "local"is ignored in feature mode (/init-feature). Feature worktrees always use global state under~/.config/opencode/worktrees/features/.
node_modules is symlinkedWhen a worktree is created, node_modules is symlinked from the main repo root. This means:
npm install needed per worktree — saves disk and timenode_modules and is immediately available in all other worktreesnode_modules does not exist yet, the symlink is skipped silently; run npm install in the main repo firstAll worktrees of a repo share the same .git/hooks directory. Installing owt hook --local or owt hook --worktree affects all worktrees of that repo, not just one. This is a git design decision, not a limitation of owt.
For per-repo isolation, use --local. For machine-wide isolation, use --global.
--no-verify bypasses all hooksRunning git commit --no-verify skips the commit-msg hook entirely. Co-authored-by will not be stripped. Avoid --no-verify unless intentional.
Git will refuse to checkout the same branch in two worktrees at the same time:
fatal: 'feature/WIN-123' is already checked out at '/path/to/worktree'
This is intentional and protects you from editing the same branch in two places.
Display names and session metadata are stored in worktree-sessions.json. Its location depends on stateLocation:
global (default): ~/.config/opencode/worktrees/repos/<repo-id>/worktree-sessions.jsonlocal: <project>/.opencode/worktree-sessions.jsonIf display names stop showing, check that this file exists and is valid JSON. See Configuration to change the location.
plugins/
└── worktree-plugin.js OpenCode plugin — all tools + system prompt injection
command/
├── init-worktree.md /init-worktree slash command
├── list-worktrees.md /list-worktrees slash command
├── open-worktree.md /open-worktree slash command
├── owt-current.md /owt-current slash command
├── init-feature.md /init-feature slash command (v4)
├── list-features.md /list-features slash command (v4)
└── delete-feature.md /delete-feature slash command (v4)
scripts/
└── owt.sh Terminal manager script (v2)
@opencode-ai/plugin >= 1.4.6)FAQs
Manage git worktrees across parallel OpenCode sessions — plugin + CLI
The npm package @nano-step/opencode-worktree-plugin receives a total of 5 weekly downloads. As such, @nano-step/opencode-worktree-plugin popularity was classified as not popular.
We found that @nano-step/opencode-worktree-plugin demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.

Security News
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.

Security News
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.