Skip to content

feat(session): add support for per-session working directories#9365

Open
ssmirr wants to merge 1 commit intoanomalyco:devfrom
ssmirr:feat/session-directory-support
Open

feat(session): add support for per-session working directories#9365
ssmirr wants to merge 1 commit intoanomalyco:devfrom
ssmirr:feat/session-directory-support

Conversation

@ssmirr
Copy link
Copy Markdown

@ssmirr ssmirr commented Jan 19, 2026

What does this PR do?

Adds Session.directory.get()/.set() so sessions can have their own working directory instead of always using Instance.directory. This enables plugins to customize where each session operates, supporting use cases like:

  • Git worktrees (isolated branches per session)
  • Sandboxed environments
  • Custom project layouts
    Updates all tools (bash, edit, write, grep, glob, ls, lsp, apply_patch) to use the session's directory.

Changes

  • session/index.ts: Add Session.directory accessor with get() and set() methods
  • All tools: Update to use Session.directory.get() instead of Instance.directory
  • prompt.ts: Set Session.directory at the start of prompt loop and shell command execution
  • system.ts: Show Session.directory in environment info
  • compaction.ts: Use Session.directory in path.cwd

Backward Compatibility

Session.directory.get() falls back to Instance.directory if not set, so existing behavior is unchanged.

Note

This PR works standalone but is designed to pair with a session.creating plugin hook (#9361), which would allow plugins to set custom directories during session creation.

How did you verify your code works?

  1. Added unit tests in packages/opencode/test/session/directory.test.ts
  2. Manual testing with a plugin that sets custom session directories
  3. Verified tools operate in the correct directory
  4. Verified sessions without a custom directory still default to Instance.directory

closes #9366

Add Session.directory.get()/set() to allow sessions to have their own
working directory, separate from Instance.directory. This enables use
cases like:
- Git worktrees per session
- Sandboxed directories
- Custom project layouts

Changes:
- Add Session.directory accessor in session/index.ts
- Update all tools (bash, edit, write, grep, glob, ls, lsp, apply_patch)
  to use Session.directory.get() instead of Instance.directory
- Update prompt.ts to set Session.directory from session.directory
  at the start of the prompt loop and shell command execution
- Update system.ts to show Session.directory in environment info
- Update compaction.ts to use Session.directory in path.cwd

Session.directory falls back to Instance.directory if not set,
ensuring backward compatibility.
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search, I found one related PR that is closely connected to the current PR:

Related PR (Companion Feature):

No duplicate PRs found addressing the same feature.

@simonsmallchua
Copy link
Copy Markdown

simonsmallchua commented Jan 31, 2026

Fully support/keen on this feature.
(Not sure if this is the right place to put a comment like this)

@MrTravisB
Copy link
Copy Markdown

This would be a great feature to have to be able to do soemthing like /new /new/working/directory

I know +1 comments not great but want to bump this up.

@h0jeZvgoxFepBQ2C
Copy link
Copy Markdown

cant wait for this feature!

@saeed-4810
Copy link
Copy Markdown

Cant wait for the feature

@andreafspeziale
Copy link
Copy Markdown

I've been testing it locally against a real-world use case (a plugin that creates git worktrees and binds sessions to them) and can confirm it fixes the core issue: tools now correctly operate in the session's stored directory rather than Instance.directory.

However, there's one gap: plugin tool context is not updated

In packages/opencode/src/tool/registry.ts, when a plugin-registered tool's execute function is called, the ToolContext passed to it is constructed like this:

const pluginCtx = {
  ...toolCtx,
  directory: ctx.directory,   // <-- still uses Instance context, not session directory
  worktree: ctx.worktree,
}

ctx.directory here comes from the InstanceContext captured at init time, it reflects the directory OpenCode was started with, not the session's directory. This means that while all built-in tools (bash, read, edit, glob, grep, etc.) will correctly use Session.directory.get() after this PR, any tool registered by a plugin will still receive the startup directory in its ToolContext.directory.

This matters for any plugin that:

  • uses context.directory to resolve file paths or execute commands
  • creates child processes scoped to the session's working directory
  • makes decisions based on the current project directory (e.g., reading project config, resolving relative paths, interacting with git)

Essentially, any plugin that relies on ToolContext.directory to know "where am I operating" would get stale information for sessions with custom directories (worktrees, sandboxes, or any session created via the API with a directory query parameter).

The fix would be a one-liner in registry.ts:

 const pluginCtx = {
   ...toolCtx,
-  directory: ctx.directory,
+  directory: Session.directory.get(),
   worktree: ctx.worktree,
 }

with the corresponding import:

+import { Session } from "../session"

This ensures plugin tools get the same per-session directory that built-in tools use, maintaining consistency across the entire tool surface.

I've tested this additional change locally alongside the rest of the PR and it works as expected — plugin tools receive the correct worktree path when operating within a session that has a custom directory.

@andreafspeziale
Copy link
Copy Markdown

@ssmirr would you be willing to include this one-liner fix as part of this PR?

@andreafspeziale
Copy link
Copy Markdown

@ssmirr would you be able to also rebase and resolve the conflicts so the team can merge and release this? Most of the conflict work is in compaction.ts. Alternatively, I can open a new PR from dev that takes your concept and pushes it forward

@ssmirr
Copy link
Copy Markdown
Author

ssmirr commented Mar 28, 2026

@andreafspeziale Thank you for trying my PR and investigating this further!
I would be happy to take care of that and update this PR, but I am not sure if the team wants this in general. Nobody from opencode team commented here :(

I recently found a similar experimental feature they've been working on that introduces /workspace command. You can enable and try it by setting OPENCODE_EXPERIMENTAL_WORKSPACES=true environment variable. What do you think of that?

@andreafspeziale
Copy link
Copy Markdown

I recently found a similar experimental feature they've been working on that introduces /workspace command. You can enable and try it by setting OPENCODE_EXPERIMENTAL_WORKSPACES=true environment variable

@ssmirr Thanks for the tip. Dax mentioned it in a tweet (https://x.com/thdxr/status/2035513929489617353?s=20) but I hadn't spotted it.

I'd say the workspaces feature is a more complete architectural solution. That said, this PR and workspaces are complementary, not conflicting.

Your PR solves the immediate problem for anyone using the session.create({ directory }) API today, which plugins like open-trees rely on.

It's also worth noting that workspaces don't currently support scoping a session to a subdirectory within a worktree, which is needed for monorepo and multi-repo project structures ( I'll be looking into it more closely to eventually contribute). With per-session directory support in place, plugins like open-trees can implement that on their own without waiting for native workspace support.

After rebase and resolve the conflicts we can reach out to the team to see what they think. Let me know your thoughts!

@andreafspeziale
Copy link
Copy Markdown

Following up on the above, I gave the workspaces feature a quick try. It’s actually very early and quite buggy at the moment. I recorded a short demo here:

workspace.mov

Here’s another video showing a locally patched version of both OpenCode and open-trees:

open-trees.mov

In this setup, the developer experience feels very smooth and quite enjoyable, in my opinion.

Hopefully we can get the team’s attention on this, they might have a preference on whether to refine the existing PR or open a new one from scratch.

@andreafspeziale
Copy link
Copy Markdown

Sorry for being persistent, but I invested more time testing this against a real monorepo/multi-repo workflow to see if per-session directories would work out-of-box for subdirectory scoping.

They don't. I had to patch several additional things locally to get subagents and supporting systems working correctly:

  • session/index.ts: Session.create and Session.fork use Instance.directory instead of Session.directory.get() when creating child sessions, so all subagent sessions (Task tool) inherit the wrong working directory

  • tool/registry.ts: plugin tool context still passes ctx.directory (Instance-scoped) instead of Session.directory.get(), so plugin tools receive stale directory info

  • session/instruction.ts: AGENTS.md and custom instruction resolution searches from the Instance directory, not the session's

  • format/formatter.ts and format/index.ts: formatter config lookups and execution use Instance directory instead of session directory

Even with all of those patched, there are still gaps that require architectural changes and can't be solved at the session level:

  • Right panel changed files (file/index.ts): the file tracking/diff system is InstanceState-scoped, so it always shows diffs against the repo root, not the worktree

  • Right panel branch name: TUI header reads Instance.worktree and the main repo branch, ignoring the session's directory/branch

So the scope of this PR alone won't cover the full worktree experience I was hoping for.

That said, it's still relevant and valuable for basic single-repo layouts where you just need sessions to operate in a different directory.

Would love to see this rebased with at least the registry fix included, and ideally the subagent session creation fix (Session.create and Session.fork in session/index.ts using Session.directory.get() instead of Instance.directory. Without it any workflow that spawns child sessions via the Task tool will have those child sessions operating in the wrong directory).

Those two are one-liners that make a big difference for plugin workflows.

For the full OpenCode experience (changed files, path, branch, etc.) you can have the agents manage your worktrees, cd into the worktree path or sub-path, and spin up a separate instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Allow sessions to have custom working directories

6 participants