mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
first attempt
This commit is contained in:
parent
f9e85a5e84
commit
6ac68a765b
5 changed files with 138 additions and 19 deletions
|
|
@ -10,13 +10,26 @@ export interface TaskProviderState {
|
|||
mode?: string
|
||||
}
|
||||
|
||||
export interface InitTaskOptions {
|
||||
mode_slug?: string
|
||||
enableDiff?: boolean
|
||||
enableCheckpoints?: boolean
|
||||
fuzzyMatchThreshold?: number
|
||||
consecutiveMistakeLimit?: number
|
||||
experiments?: any
|
||||
}
|
||||
export interface TaskProviderLike {
|
||||
readonly cwd: string
|
||||
|
||||
getCurrentCline(): TaskLike | undefined
|
||||
getCurrentTaskStack(): string[]
|
||||
|
||||
initClineWithTask(text?: string, images?: string[], parentTask?: TaskLike): Promise<TaskLike>
|
||||
initClineWithTask(
|
||||
text?: string,
|
||||
images?: string[],
|
||||
parentTask?: TaskLike,
|
||||
options?: InitTaskOptions,
|
||||
): Promise<TaskLike>
|
||||
cancelTask(): Promise<void>
|
||||
clearTask(): Promise<void>
|
||||
postStateToWebview(): Promise<void>
|
||||
|
|
|
|||
|
|
@ -285,15 +285,20 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
}
|
||||
|
||||
this.taskId = historyItem ? historyItem.id : crypto.randomUUID()
|
||||
|
||||
// Normal use-case is usually retry similar history task with new workspace.
|
||||
this.workspacePath = parentTask
|
||||
? parentTask.workspacePath
|
||||
: getWorkspacePath(path.join(os.homedir(), "Desktop"))
|
||||
|
||||
this.instanceId = crypto.randomUUID().slice(0, 8)
|
||||
this.taskNumber = -1
|
||||
|
||||
// Initialize workspacePath FIRST before any code that uses this.cwd
|
||||
// This MUST happen before creating RooIgnoreController or RooProtectedController
|
||||
const defaultPath = path.join(os.homedir(), "Desktop")
|
||||
const workspaceFromVSCode = getWorkspacePath(defaultPath)
|
||||
|
||||
// Ensure workspacePath is never undefined or empty - use the VSCode workspace or fallback
|
||||
// Check for both undefined and empty string from parentTask
|
||||
const parentWorkspace = parentTask?.workspacePath
|
||||
this.workspacePath = parentWorkspace && parentWorkspace.trim() !== "" ? parentWorkspace : workspaceFromVSCode
|
||||
|
||||
// Now create controllers with properly initialized workspacePath
|
||||
this.rooIgnoreController = new RooIgnoreController(this.cwd)
|
||||
this.rooProtectedController = new RooProtectedController(this.cwd)
|
||||
this.fileContextTracker = new FileContextTracker(provider, this.taskId)
|
||||
|
|
@ -2468,6 +2473,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// Getters
|
||||
|
||||
public get cwd() {
|
||||
if (!this.workspacePath) {
|
||||
// Return a fallback to prevent crashes
|
||||
return path.join(os.homedir(), "Desktop")
|
||||
}
|
||||
return this.workspacePath
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
type TerminalActionPromptType,
|
||||
type HistoryItem,
|
||||
type CloudUserInfo,
|
||||
type InitTaskOptions,
|
||||
RooCodeEventName,
|
||||
requestyDefaultModelId,
|
||||
openRouterDefaultModelId,
|
||||
|
|
@ -77,7 +78,7 @@ import { forceFullModelDetailsLoad, hasLoadedFullDetails } from "../../api/provi
|
|||
import { ContextProxy } from "../config/ContextProxy"
|
||||
import { ProviderSettingsManager } from "../config/ProviderSettingsManager"
|
||||
import { CustomModesManager } from "../config/CustomModesManager"
|
||||
import { Task, TaskOptions } from "../task/Task"
|
||||
import { Task } from "../task/Task"
|
||||
import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt"
|
||||
|
||||
import { webviewMessageHandler } from "./webviewMessageHandler"
|
||||
|
|
@ -698,17 +699,7 @@ export class ClineProvider
|
|||
// from the stack and the caller is resumed in this way we can have a chain
|
||||
// of tasks, each one being a sub task of the previous one until the main
|
||||
// task is finished.
|
||||
public async initClineWithTask(
|
||||
text?: string,
|
||||
images?: string[],
|
||||
parentTask?: Task,
|
||||
options: Partial<
|
||||
Pick<
|
||||
TaskOptions,
|
||||
"enableDiff" | "enableCheckpoints" | "fuzzyMatchThreshold" | "consecutiveMistakeLimit" | "experiments"
|
||||
>
|
||||
> = {},
|
||||
) {
|
||||
public async initClineWithTask(text?: string, images?: string[], parentTask?: Task, options: InitTaskOptions = {}) {
|
||||
const {
|
||||
apiConfiguration,
|
||||
organizationAllowList,
|
||||
|
|
@ -724,6 +715,31 @@ export class ClineProvider
|
|||
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
|
||||
}
|
||||
|
||||
// Bridge: If a mode_slug is provided by the cloud extension bridge, honor it before creating the task
|
||||
// This ensures the task initializes with the correct mode and associated provider profile
|
||||
try {
|
||||
const modeSlugFromBridge: string | undefined = (options as any)?.mode_slug
|
||||
if (typeof modeSlugFromBridge === "string" && modeSlugFromBridge.trim().length > 0) {
|
||||
const customModes = await this.customModesManager.getCustomModes()
|
||||
const targetMode = getModeBySlug(modeSlugFromBridge, customModes)
|
||||
if (targetMode) {
|
||||
// Switch provider/global mode first so Task reads it during initialization
|
||||
await this.handleModeSwitch(targetMode.slug)
|
||||
this.log(`[initClineWithTask] Applied mode from bridge: '${targetMode.slug}'`)
|
||||
} else {
|
||||
this.log(
|
||||
`[initClineWithTask] Ignoring invalid mode_slug from bridge: '${modeSlugFromBridge}'. Falling back to current mode.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.log(
|
||||
`[initClineWithTask] Failed to apply mode_slug from bridge: ${
|
||||
err instanceof Error ? err.message : String(err)
|
||||
}`,
|
||||
)
|
||||
}
|
||||
|
||||
const task = new Task({
|
||||
provider: this,
|
||||
apiConfiguration,
|
||||
|
|
|
|||
|
|
@ -2206,6 +2206,84 @@ describe("ClineProvider", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
describe("Bridge mode_slug handling", () => {
|
||||
let provider: ClineProvider
|
||||
let mockContext: vscode.ExtensionContext
|
||||
let mockOutputChannel: vscode.OutputChannel
|
||||
let mockWebviewView: vscode.WebviewView
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
mockContext = {
|
||||
extensionPath: "/test/path",
|
||||
extensionUri: {} as vscode.Uri,
|
||||
globalState: {
|
||||
get: vi.fn(),
|
||||
update: vi.fn(),
|
||||
keys: vi.fn().mockReturnValue([]),
|
||||
},
|
||||
secrets: {
|
||||
get: vi.fn(),
|
||||
store: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
},
|
||||
subscriptions: [],
|
||||
extension: {
|
||||
packageJSON: { version: "1.0.0" },
|
||||
},
|
||||
globalStorageUri: {
|
||||
fsPath: "/test/storage/path",
|
||||
},
|
||||
} as unknown as vscode.ExtensionContext
|
||||
|
||||
mockOutputChannel = {
|
||||
appendLine: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
dispose: vi.fn(),
|
||||
} as unknown as vscode.OutputChannel
|
||||
|
||||
mockWebviewView = {
|
||||
webview: {
|
||||
postMessage: vi.fn(),
|
||||
html: "",
|
||||
options: {},
|
||||
onDidReceiveMessage: vi.fn(),
|
||||
asWebviewUri: vi.fn(),
|
||||
cspSource: "vscode-webview://test-csp-source",
|
||||
},
|
||||
visible: true,
|
||||
onDidDispose: vi.fn(),
|
||||
onDidChangeVisibility: vi.fn(),
|
||||
} as unknown as vscode.WebviewView
|
||||
|
||||
provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
|
||||
})
|
||||
|
||||
it("applies mode_slug from bridge options when starting task", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
|
||||
// Spy on handleModeSwitch to ensure it's invoked with the bridge-provided mode
|
||||
const handleModeSwitchSpy = vi.spyOn(provider, "handleModeSwitch").mockResolvedValue(undefined as any)
|
||||
|
||||
// Ensure getModeBySlug returns a valid mode for the provided slug
|
||||
const { getModeBySlug } = await import("../../../shared/modes")
|
||||
vi.mocked(getModeBySlug).mockReturnValueOnce({
|
||||
slug: "architect",
|
||||
name: "Architect Mode",
|
||||
roleDefinition: "You are an architect",
|
||||
groups: ["read", "edit"] as any,
|
||||
} as any)
|
||||
|
||||
// Pass mode_slug through the options object (as provided by the bridge package)
|
||||
await provider.initClineWithTask("Started from bridge", undefined, undefined, {
|
||||
experiments: {},
|
||||
mode_slug: "architect",
|
||||
} as any)
|
||||
|
||||
expect(handleModeSwitchSpy).toHaveBeenCalledWith("architect")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Project MCP Settings", () => {
|
||||
let provider: ClineProvider
|
||||
|
|
|
|||
|
|
@ -97,11 +97,13 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
text,
|
||||
images,
|
||||
newTab,
|
||||
mode_slug,
|
||||
}: {
|
||||
configuration: RooCodeSettings
|
||||
text?: string
|
||||
images?: string[]
|
||||
newTab?: boolean
|
||||
mode_slug?: string
|
||||
}) {
|
||||
let provider: ClineProvider
|
||||
|
||||
|
|
@ -150,6 +152,7 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
|
||||
const cline = await provider.initClineWithTask(text, images, undefined, {
|
||||
consecutiveMistakeLimit: Number.MAX_SAFE_INTEGER,
|
||||
mode_slug,
|
||||
})
|
||||
|
||||
if (!cline) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue