Skip to content

feat(workspace): add rootResolution setting for multi-root workspaces#538

Merged
edelauna merged 4 commits into
Zoo-Code-Org:mainfrom
simurg79:feat/workspace-root-resolution
Jun 12, 2026
Merged

feat(workspace): add rootResolution setting for multi-root workspaces#538
edelauna merged 4 commits into
Zoo-Code-Org:mainfrom
simurg79:feat/workspace-root-resolution

Conversation

@simurg79

@simurg79 simurg79 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports the workspace root resolution feature from simurg79/Roo-Code (commit d89c1ff) into Zoo-Code.

Introduces a new zoo-code.workspace.rootResolution VS Code setting that controls how Zoo resolves the "project root" in a multi-root workspace (used to locate .roomodes, .roo/mcp.json, .roo/rules/, and other project-scoped configuration):

Value Behavior
activeEditor (default) Prefer the workspace folder containing the active editor; fall back to workspaceFolders[0]. Preserves legacy behavior.
firstFolder Always use workspaceFolders[0]. Deterministic — independent of which file is focused.

The default preserves today's behavior exactly, so this is a non-breaking, opt-in change.

Motivation

The historic active-editor heuristic is convenient but non-deterministic: switching the focused file between roots silently changes which project config is picked up, and it can disagree with the marketplace installers (which always target workspaceFolders[0]). firstFolder gives a stable root for CI/automation and multi-root setups where only one folder is the real project.

Changes

Port notes

  • Settings namespace adapted from the fork's roo-cline.* to Zoo-Code's zoo-code.*.
  • The test reads the shared vscode mock via resolve.alias (mutation-based) to match Zoo-Code's src/vitest.config.ts.

Testing

cd src && npx vitest run utils/__tests__/path.spec.ts30 passed. Both modified JSON files validated as parseable. Pre-commit lint hooks passed.

Summary by CodeRabbit

  • New Features
    • Workspace root resolution setting for multi-root workspaces: Users can now choose how the extension determines the workspace root. Two options: (1) active editor—determines root based on the currently active file (default), or (2) first folder—always uses the first workspace folder. Supported in 17 languages.

Introduces a new zoo-code.workspace.rootResolution VS Code setting with two strategies:

- activeEditor (default): legacy behavior - prefer the workspace folder containing the active editor, fall back to workspaceFolders[0].

- firstFolder: deterministic - always use workspaceFolders[0], independent of the focused file.

Updates getWorkspacePath() and getWorkspacePathForContext() in src/utils/path.ts to honor the setting, registers it in src/package.json with localized strings in src/package.nls.json, expands src/utils/__tests__/path.spec.ts with focused unit tests for both strategies, and adds a design doc at docs/design/workspace-root-resolution.md.

Ported from simurg79/Roo-Code commit d89c1ff (settings namespace adapted roo-cline -> zoo-code).
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 00ee3b3e-0038-4701-b359-18d882ba87c3

📥 Commits

Reviewing files that changed from the base of the PR and between 93543b8 and d647011.

📒 Files selected for processing (3)
  • src/package.json
  • src/utils/__tests__/path.spec.ts
  • src/utils/path.ts
✅ Files skipped from review due to trivial changes (1)
  • src/utils/path.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/package.json
  • src/utils/tests/path.spec.ts

📝 Walkthrough

Walkthrough

This PR adds a configurable zoo-code.workspace.rootResolution setting that lets users choose how Zoo Code determines the workspace root in multi-root workspaces. The feature includes the extension schema, localized descriptions across 17 locales, safe config-reading logic, and comprehensive test coverage.

Changes

Workspace Root Resolution Feature

Layer / File(s) Summary
Configuration schema and localization
src/package.json, src/package.nls.json, src/package.nls*.json
Adds zoo-code.workspace.rootResolution setting with enum values activeEditor (default) and firstFolder, and populates localized descriptions across the main NLS file and all 17 locale variants (ca, de, es, fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW).
Workspace resolution implementation
src/utils/path.ts
Introduces WorkspaceRootResolution type and getRootResolutionStrategy() helper that safely reads the configuration with fallback; updates getWorkspacePath() to deterministically return workspaceFolders[0] in firstFolder mode or prefer the active editor's workspace folder in activeEditor mode; updates getWorkspacePathForContext() to short-circuit and skip context-path workspace lookup in firstFolder mode.
Test coverage and helpers
src/utils/__tests__/path.spec.ts
Refactors mocking from static vi.mock() to a shared imported mock; adds typed setRootResolution and withWorkspaceMock helpers for test state management; expands test suites for both resolution functions to validate strategy behavior, fallback logic, exception resilience, and assertion that getWorkspaceFolder is not consulted in firstFolder mode for context-based resolution.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • taltas
  • navedmerchant
  • hannesrudolph
  • JamesRobert20

🐰 A setting blooms in the config,
Two paths now guide the root's quest—
First folder, or the editor's best,
Seventeen tongues translate the wish,
And tests ensure the path won't miss! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main change: adding a rootResolution setting for multi-root workspaces.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the feature summary, motivation, changes, testing, and port notes. However, it is missing the required template sections: Related GitHub Issue (with Closes: # linking), Pre-Submission Checklist, and some optional sections like Screenshots/Videos and Additional Notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Bertan Ari added 2 commits June 9, 2026 07:22
Adds the three settings.workspace.rootResolution.* keys to all 17 non-English package.nls locale files to fix the check-translations CI check.

@edelauna edelauna left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks for adding this, had a couple edge case comments.

Comment thread src/package.json
"default": "activeEditor",
"description": "%settings.workspace.rootResolution.description%",
"markdownDescription": "%settings.workspace.rootResolution.description%"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without an explicit "scope" this defaults to "window", which means a committed .vscode/settings.json can override it. Since this setting controls which workspace folder is used to locate .roo/mcp.json (and therefore which MCP servers get registered), could a malicious workspace settings file redirect that to an attacker-controlled folder in a multi-root workspace?

If workspace-level override should be blocked, "scope": "machine" would prevent it. If it is intentional, worth documenting the trust decision here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this setting feeds MCP-server discovery, so I've set "scope": "machine" to prevent a committed workspace settings.json from redirecting the resolved root. This intentionally disallows workspace-level overrides (the security tradeoff is deliberate).

Comment thread src/utils/path.ts Outdated
// `Package` here (path.ts is a low-level utility and we want to keep
// it free of circular dependencies on shared/* and package.json).
const value = vscode.workspace
.getConfiguration("zoo-code")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every other getConfiguration call in the codebase uses Package.name to respect the PKG_NAME build override. If this extension is ever built under a different package name, getConfiguration("zoo-code") will silently return only the supplied defaults — no error, no warning. Is the circular-dependency concern in the comment above actually verified? shared/package.ts only imports from ../package.json and doesn't look like it touches path.ts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — shared/package.ts only imports package.json, so there's no cycle. Switched to getConfiguration(Package.name) so it honors the PKG_NAME build override, and removed the inaccurate comment.

Comment thread src/utils/__tests__/path.spec.ts Outdated
@@ -63,6 +108,155 @@ describe("Path Utilities", () => {
})

it("should return undefined when outside a workspace", () => {})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test body is empty and the description looks wrong — getWorkspacePath always returns a string, never undefined. Was this meant to be filled in?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed — it was a leftover stub with an incorrect description (getWorkspacePath always returns a string). The no-workspace path is already covered by the falls back to defaultCwdPath cases.

Comment thread src/utils/__tests__/path.spec.ts Outdated
})

it("falls back to default behavior when reading the setting throws", () => {
mockWorkspace.getConfiguration = () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getConfiguration is mutated here directly but withWorkspaceMock's cleanup (lines 66–70) only restores workspaceFolders, getWorkspaceFolder, and activeTextEditor. Recovery depends on the outer beforeEach running setRootResolution(undefined) next. If this test is ever moved or the beforeEach changes, subsequent tests will throw on any config read. Worth saving/restoring getConfiguration in the finally block here, or extending withWorkspaceMock to cover it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — withWorkspaceMock now snapshots and restores getConfiguration too, and the throwing-config test routes its mock through it, so a broken mock can't leak into sibling tests regardless of beforeEach.

} finally {
restore()
}
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in activeEditor mode when contextPath is provided but getWorkspaceFolder returns null? That fallback path (the console.debug branch in path.ts:183–186) doesn't appear to be covered here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test for that case (activeEditor + contextPath set + getWorkspaceFolder → null); it covers the console.debug fallback branch and asserts it falls back to workspaceFolders[0].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not commit this, this is a working file for you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from the PR — that was a working scratch doc, not intended to ship.

… use Package.name, test mock cleanup + coverage, drop scratch design doc

@edelauna edelauna left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, this is a cool feature for multiple folders within a workspace.

@edelauna edelauna added this pull request to the merge queue Jun 12, 2026
Merged via the queue into Zoo-Code-Org:main with commit 8faea46 Jun 12, 2026
11 checks passed
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.

2 participants