Skip to content

Commit 1e403a7

Browse files
authored
fix: identify mcp and slash command config path in multiple folder workspace (#6904)
1 parent 0510c03 commit 1e403a7

File tree

9 files changed

+50
-38
lines changed

9 files changed

+50
-38
lines changed

src/activate/registerCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
9595
TelemetryService.instance.captureTitleButtonClicked("plus")
9696

9797
await visibleProvider.removeClineFromStack()
98-
await visibleProvider.postStateToWebview()
98+
await visibleProvider.refreshWorkspace()
9999
await visibleProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
100100
// Send focusInput action immediately after chatButtonClicked
101101
// This ensures the focus happens after the view has switched

src/core/mentions/index.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { isBinaryFile } from "isbinaryfile"
77
import { mentionRegexGlobal, commandRegexGlobal, unescapeSpaces } from "../../shared/context-mentions"
88

99
import { getCommitInfo, getWorkingState } from "../../utils/git"
10-
import { getWorkspacePath } from "../../utils/path"
1110

1211
import { openFile } from "../../integrations/misc/open-file"
1312
import { extractTextFromFile } from "../../integrations/misc/extract-text"
@@ -49,16 +48,11 @@ function getUrlErrorMessage(error: unknown): string {
4948
return t("common:errors.url_fetch_failed", { error: errorMessage })
5049
}
5150

52-
export async function openMention(mention?: string): Promise<void> {
51+
export async function openMention(cwd: string, mention?: string): Promise<void> {
5352
if (!mention) {
5453
return
5554
}
5655

57-
const cwd = getWorkspacePath()
58-
if (!cwd) {
59-
return
60-
}
61-
6256
if (mention.startsWith("/")) {
6357
// Slice off the leading slash and unescape any spaces in the path
6458
const relPath = unescapeSpaces(mention.slice(1))

src/core/task/Task.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export interface TaskOptions extends CreateTaskOptions {
138138
taskNumber?: number
139139
onCreated?: (task: Task) => void
140140
initialTodos?: TodoItem[]
141+
workspacePath?: string
141142
}
142143

143144
export class Task extends EventEmitter<TaskEvents> implements TaskLike {
@@ -313,6 +314,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
313314
taskNumber = -1,
314315
onCreated,
315316
initialTodos,
317+
workspacePath,
316318
}: TaskOptions) {
317319
super()
318320

@@ -333,7 +335,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
333335
// Normal use-case is usually retry similar history task with new workspace.
334336
this.workspacePath = parentTask
335337
? parentTask.workspacePath
336-
: getWorkspacePath(path.join(os.homedir(), "Desktop"))
338+
: (workspacePath ?? getWorkspacePath(path.join(os.homedir(), "Desktop")))
337339

338340
this.instanceId = crypto.randomUUID().slice(0, 8)
339341
this.taskNumber = -1

src/core/webview/ClineProvider.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,14 @@ export class ClineProvider
126126
private view?: vscode.WebviewView | vscode.WebviewPanel
127127
private clineStack: Task[] = []
128128
private codeIndexStatusSubscription?: vscode.Disposable
129-
private currentWorkspaceManager?: CodeIndexManager
129+
private codeIndexManager?: CodeIndexManager
130130
private _workspaceTracker?: WorkspaceTracker // workSpaceTracker read-only for access outside this class
131131
protected mcpHub?: McpHub // Change from private to protected
132132
private marketplaceManager: MarketplaceManager
133133
private mdmService?: MdmService
134134
private taskCreationCallback: (task: Task) => void
135135
private taskEventListeners: WeakMap<Task, Array<() => void>> = new WeakMap()
136+
private currentWorkspacePath: string | undefined
136137

137138
private recentTasksCache?: string[]
138139
private pendingOperations: Map<string, PendingEditOperation> = new Map()
@@ -152,6 +153,7 @@ export class ClineProvider
152153
mdmService?: MdmService,
153154
) {
154155
super()
156+
this.currentWorkspacePath = getWorkspacePath()
155157

156158
ClineProvider.activeInstances.add(this)
157159

@@ -792,7 +794,7 @@ export class ClineProvider
792794
this.log("Clearing webview resources for sidebar view")
793795
this.clearWebviewResources()
794796
// Reset current workspace manager reference when view is disposed
795-
this.currentWorkspaceManager = undefined
797+
this.codeIndexManager = undefined
796798
}
797799
},
798800
null,
@@ -880,6 +882,7 @@ export class ClineProvider
880882
rootTask: historyItem.rootTask,
881883
parentTask: historyItem.parentTask,
882884
taskNumber: historyItem.number,
885+
workspacePath: historyItem.workspace,
883886
onCreated: this.taskCreationCallback,
884887
enableBridge: BridgeOrchestrator.isEnabled(cloudUserInfo, remoteControlEnabled),
885888
})
@@ -1562,6 +1565,11 @@ export class ClineProvider
15621565
await this.postStateToWebview()
15631566
}
15641567

1568+
async refreshWorkspace() {
1569+
this.currentWorkspacePath = getWorkspacePath()
1570+
await this.postStateToWebview()
1571+
}
1572+
15651573
async postStateToWebview() {
15661574
const state = await this.getStateToPostToWebview()
15671575
this.postMessageToWebview({ type: "state", state })
@@ -2287,7 +2295,7 @@ export class ClineProvider
22872295
const currentManager = this.getCurrentWorkspaceCodeIndexManager()
22882296

22892297
// If the manager hasn't changed, no need to update subscription
2290-
if (currentManager === this.currentWorkspaceManager) {
2298+
if (currentManager === this.codeIndexManager) {
22912299
return
22922300
}
22932301

@@ -2298,7 +2306,7 @@ export class ClineProvider
22982306
}
22992307

23002308
// Update the current workspace manager reference
2301-
this.currentWorkspaceManager = currentManager
2309+
this.codeIndexManager = currentManager
23022310

23032311
// Subscribe to the new manager's progress updates if it exists
23042312
if (currentManager) {
@@ -2659,7 +2667,7 @@ export class ClineProvider
26592667
}
26602668

26612669
public get cwd() {
2662-
return getWorkspacePath()
2670+
return this.currentWorkspacePath || getWorkspacePath()
26632671
}
26642672

26652673
/**

src/core/webview/webviewMessageHandler.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ export const webviewMessageHandler = async (
6969
const updateGlobalState = async <K extends keyof GlobalState>(key: K, value: GlobalState[K]) =>
7070
await provider.contextProxy.setValue(key, value)
7171

72+
const getCurrentCwd = () => {
73+
return provider.getCurrentTask()?.cwd || provider.cwd
74+
}
7275
/**
7376
* Shared utility to find message indices based on timestamp
7477
*/
@@ -912,10 +915,14 @@ export const webviewMessageHandler = async (
912915
saveImage(message.dataUri!)
913916
break
914917
case "openFile":
915-
openFile(message.text!, message.values as { create?: boolean; content?: string; line?: number })
918+
let filePath: string = message.text!
919+
if (!path.isAbsolute(filePath)) {
920+
filePath = path.join(getCurrentCwd(), filePath)
921+
}
922+
openFile(filePath, message.values as { create?: boolean; content?: string; line?: number })
916923
break
917924
case "openMention":
918-
openMention(message.text)
925+
openMention(getCurrentCwd(), message.text)
919926
break
920927
case "openExternal":
921928
if (message.url) {
@@ -1010,8 +1017,8 @@ export const webviewMessageHandler = async (
10101017
return
10111018
}
10121019

1013-
const workspaceFolder = vscode.workspace.workspaceFolders[0]
1014-
const rooDir = path.join(workspaceFolder.uri.fsPath, ".roo")
1020+
const workspaceFolder = getCurrentCwd()
1021+
const rooDir = path.join(workspaceFolder, ".roo")
10151022
const mcpPath = path.join(rooDir, "mcp.json")
10161023

10171024
try {
@@ -1660,7 +1667,7 @@ export const webviewMessageHandler = async (
16601667
}
16611668
break
16621669
case "searchCommits": {
1663-
const cwd = provider.cwd
1670+
const cwd = getCurrentCwd()
16641671
if (cwd) {
16651672
try {
16661673
const commits = await searchCommits(message.query || "", cwd)
@@ -1678,7 +1685,7 @@ export const webviewMessageHandler = async (
16781685
break
16791686
}
16801687
case "searchFiles": {
1681-
const workspacePath = getWorkspacePath()
1688+
const workspacePath = getCurrentCwd()
16821689

16831690
if (!workspacePath) {
16841691
// Handle case where workspace path is not available
@@ -2662,7 +2669,7 @@ export const webviewMessageHandler = async (
26622669
case "requestCommands": {
26632670
try {
26642671
const { getCommands } = await import("../../services/command/commands")
2665-
const commands = await getCommands(provider.cwd || "")
2672+
const commands = await getCommands(getCurrentCwd())
26662673

26672674
// Convert to the format expected by the frontend
26682675
const commandList = commands.map((command) => ({
@@ -2691,7 +2698,7 @@ export const webviewMessageHandler = async (
26912698
try {
26922699
if (message.text) {
26932700
const { getCommand } = await import("../../services/command/commands")
2694-
const command = await getCommand(provider.cwd || "", message.text)
2701+
const command = await getCommand(getCurrentCwd(), message.text)
26952702

26962703
if (command && command.filePath) {
26972704
openFile(command.filePath)
@@ -2711,7 +2718,7 @@ export const webviewMessageHandler = async (
27112718
try {
27122719
if (message.text && message.values?.source) {
27132720
const { getCommand } = await import("../../services/command/commands")
2714-
const command = await getCommand(provider.cwd || "", message.text)
2721+
const command = await getCommand(getCurrentCwd(), message.text)
27152722

27162723
if (command && command.filePath) {
27172724
// Delete the command file
@@ -2743,8 +2750,12 @@ export const webviewMessageHandler = async (
27432750
const globalConfigDir = path.join(os.homedir(), ".roo")
27442751
commandsDir = path.join(globalConfigDir, "commands")
27452752
} else {
2753+
if (!vscode.workspace.workspaceFolders?.length) {
2754+
vscode.window.showErrorMessage(t("common:errors.no_workspace"))
2755+
return
2756+
}
27462757
// Project commands
2747-
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
2758+
const workspaceRoot = getCurrentCwd()
27482759
if (!workspaceRoot) {
27492760
vscode.window.showErrorMessage(t("common:errors.no_workspace_for_project_command"))
27502761
break
@@ -2824,7 +2835,7 @@ export const webviewMessageHandler = async (
28242835

28252836
// Refresh commands list
28262837
const { getCommands } = await import("../../services/command/commands")
2827-
const commands = await getCommands(provider.cwd || "")
2838+
const commands = await getCommands(getCurrentCwd() || "")
28282839
const commandList = commands.map((command) => ({
28292840
name: command.name,
28302841
source: command.source,

src/integrations/workspace/WorkspaceTracker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class WorkspaceTracker {
1717
private resetTimer: NodeJS.Timeout | null = null
1818

1919
get cwd() {
20-
return getWorkspacePath()
20+
return this.providerRef?.deref()?.cwd ?? getWorkspacePath()
2121
}
2222
constructor(provider: ClineProvider) {
2323
this.providerRef = new WeakRef(provider)

src/services/code-index/manager.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as vscode from "vscode"
2-
import { getWorkspacePath } from "../../utils/path"
32
import { ContextProxy } from "../../core/config/ContextProxy"
43
import { VectorStoreSearchResult } from "./interfaces"
54
import { IndexingState } from "./interfaces/manager"
@@ -133,7 +132,7 @@ export class CodeIndexManager {
133132
}
134133

135134
// 3. Check if workspace is available
136-
const workspacePath = getWorkspacePath()
135+
const workspacePath = this.workspacePath
137136
if (!workspacePath) {
138137
this._stateManager.setSystemState("Standby", "No workspace folder open")
139138
return { requiresRestart }
@@ -306,7 +305,7 @@ export class CodeIndexManager {
306305
)
307306

308307
const ignoreInstance = ignore()
309-
const workspacePath = getWorkspacePath()
308+
const workspacePath = this.workspacePath
310309

311310
if (!workspacePath) {
312311
this._stateManager.setSystemState("Standby", "")

src/services/code-index/vector-store/qdrant-client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class QdrantVectorStore implements IVectorStore {
1717
private client: QdrantClient
1818
private readonly collectionName: string
1919
private readonly qdrantUrl: string = "http://localhost:6333"
20+
private readonly workspacePath: string
2021

2122
/**
2223
* Creates a new Qdrant vector store
@@ -29,6 +30,7 @@ export class QdrantVectorStore implements IVectorStore {
2930

3031
// Store the resolved URL for our property
3132
this.qdrantUrl = parsedUrl
33+
this.workspacePath = workspacePath
3234

3335
try {
3436
const urlObj = new URL(parsedUrl)
@@ -457,7 +459,7 @@ export class QdrantVectorStore implements IVectorStore {
457459
return
458460
}
459461

460-
const workspaceRoot = getWorkspacePath()
462+
const workspaceRoot = this.workspacePath
461463

462464
// Build filters using pathSegments to match the indexed fields
463465
const filters = filePaths.map((filePath) => {

src/services/mcp/McpHub.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
McpToolCallResponse,
3131
} from "../../shared/mcp"
3232
import { fileExistsAtPath } from "../../utils/fs"
33-
import { arePathsEqual } from "../../utils/path"
33+
import { arePathsEqual, getWorkspacePath } from "../../utils/path"
3434
import { injectVariables } from "../../utils/config"
3535

3636
// Discriminated union for connection states
@@ -349,7 +349,7 @@ export class McpHub {
349349
return
350350
}
351351

352-
const workspaceFolder = vscode.workspace.workspaceFolders[0]
352+
const workspaceFolder = this.providerRef.deref()?.cwd ?? getWorkspacePath()
353353
const projectMcpPattern = new vscode.RelativePattern(workspaceFolder, ".roo/mcp.json")
354354

355355
// Create a file system watcher for the project MCP file pattern
@@ -551,12 +551,8 @@ export class McpHub {
551551

552552
// Get project-level MCP configuration path
553553
private async getProjectMcpPath(): Promise<string | null> {
554-
if (!vscode.workspace.workspaceFolders?.length) {
555-
return null
556-
}
557-
558-
const workspaceFolder = vscode.workspace.workspaceFolders[0]
559-
const projectMcpDir = path.join(workspaceFolder.uri.fsPath, ".roo")
554+
const workspacePath = this.providerRef.deref()?.cwd ?? getWorkspacePath()
555+
const projectMcpDir = path.join(workspacePath, ".roo")
560556
const projectMcpPath = path.join(projectMcpDir, "mcp.json")
561557

562558
try {

0 commit comments

Comments
 (0)