From d95a8764f60d289eea2da0fba7decb55efe4561e Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sun, 22 Feb 2026 22:29:20 +0000 Subject: [PATCH] feat: add weave semantic merge driver integration for worktrees --- .../__tests__/weave-merge-driver.spec.ts | 264 +++++++++++++++ packages/core/src/worktree/index.ts | 7 + .../core/src/worktree/weave-merge-driver.ts | 312 ++++++++++++++++++ packages/types/src/vscode-extension-host.ts | 13 + src/core/webview/webviewMessageHandler.ts | 56 ++++ src/core/webview/worktree/handlers.ts | 27 +- src/core/webview/worktree/index.ts | 3 + 7 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/worktree/__tests__/weave-merge-driver.spec.ts create mode 100644 packages/core/src/worktree/weave-merge-driver.ts diff --git a/packages/core/src/worktree/__tests__/weave-merge-driver.spec.ts b/packages/core/src/worktree/__tests__/weave-merge-driver.spec.ts new file mode 100644 index 00000000000..e41dfd9db47 --- /dev/null +++ b/packages/core/src/worktree/__tests__/weave-merge-driver.spec.ts @@ -0,0 +1,264 @@ +import * as fs from "fs/promises" +import * as os from "os" +import * as path from "path" + +import { WeaveMergeDriverService, WEAVE_SUPPORTED_EXTENSIONS, buildGitattributesLines } from "../weave-merge-driver.js" + +// Helper to run git commands in a directory using execFile for proper argument handling +import { execFileSync } from "child_process" + +function execGit(cwd: string, args: string[]): void { + execFileSync("git", args, { cwd, stdio: "pipe" }) +} + +describe("WeaveMergeDriverService", () => { + let service: WeaveMergeDriverService + let tempDir: string + + beforeEach(async () => { + service = new WeaveMergeDriverService() + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "weave-test-")) + }) + + afterEach(async () => { + await fs.rm(tempDir, { recursive: true, force: true }) + }) + + describe("buildGitattributesLines", () => { + it("should build gitattributes lines for default extensions", () => { + const lines = buildGitattributesLines() + expect(lines.length).toBe(WEAVE_SUPPORTED_EXTENSIONS.length) + expect(lines[0]).toBe("*.py merge=weave") + expect(lines[1]).toBe("*.js merge=weave") + expect(lines[2]).toBe("*.jsx merge=weave") + expect(lines[3]).toBe("*.ts merge=weave") + }) + + it("should build gitattributes lines for custom extensions", () => { + const lines = buildGitattributesLines(["*.py", "*.rs"]) + expect(lines).toEqual(["*.py merge=weave", "*.rs merge=weave"]) + }) + }) + + describe("WEAVE_SUPPORTED_EXTENSIONS", () => { + it("should include common language extensions", () => { + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.py") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.js") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.ts") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.tsx") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.rs") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.go") + expect(WEAVE_SUPPORTED_EXTENSIONS).toContain("*.java") + }) + }) + + describe("isInstalled", () => { + it("should return a boolean", async () => { + const result = await service.isInstalled() + expect(typeof result).toBe("boolean") + }) + }) + + describe("getVersion", () => { + it("should return undefined when weave is not installed", async () => { + // Override PATH to ensure weave isn't found + const originalPath = process.env.PATH + process.env.PATH = "" + try { + const version = await service.getVersion() + expect(version).toBeUndefined() + } finally { + process.env.PATH = originalPath + } + }) + }) + + describe("isConfiguredInGitConfig", () => { + it("should return false for a repo without weave config", async () => { + execGit(tempDir, ["init"]) + execGit(tempDir, ["config", "user.email", "test@test.com"]) + execGit(tempDir, ["config", "user.name", "Test"]) + + const result = await service.isConfiguredInGitConfig(tempDir) + expect(result).toBe(false) + }) + + it("should return true when weave merge driver is configured", async () => { + execGit(tempDir, ["init"]) + execGit(tempDir, ["config", "user.email", "test@test.com"]) + execGit(tempDir, ["config", "user.name", "Test"]) + execGit(tempDir, ["config", "--local", "merge.weave.driver", "weave merge %O %A %B %P"]) + + const result = await service.isConfiguredInGitConfig(tempDir) + expect(result).toBe(true) + }) + + it("should return false for a non-git directory", async () => { + const result = await service.isConfiguredInGitConfig(tempDir) + expect(result).toBe(false) + }) + }) + + describe("isConfiguredInGitattributes", () => { + it("should return false when .gitattributes does not exist", async () => { + execGit(tempDir, ["init"]) + + const result = await service.isConfiguredInGitattributes(tempDir) + expect(result).toBe(false) + }) + + it("should return false when .gitattributes exists but has no weave entries", async () => { + execGit(tempDir, ["init"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.txt text\n") + + const result = await service.isConfiguredInGitattributes(tempDir) + expect(result).toBe(false) + }) + + it("should return true when .gitattributes contains weave entries", async () => { + execGit(tempDir, ["init"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.py merge=weave\n*.ts merge=weave\n") + + const result = await service.isConfiguredInGitattributes(tempDir) + expect(result).toBe(true) + }) + }) + + describe("getStatus", () => { + it("should return full status for an unconfigured repo", async () => { + execGit(tempDir, ["init"]) + + const status = await service.getStatus(tempDir) + expect(status.isConfiguredInGitConfig).toBe(false) + expect(status.isConfiguredInGitattributes).toBe(false) + expect(status.isFullyConfigured).toBe(false) + expect(typeof status.isInstalled).toBe("boolean") + }) + + it("should report isFullyConfigured when both config and gitattributes are set", async () => { + execGit(tempDir, ["init"]) + execGit(tempDir, ["config", "--local", "merge.weave.driver", "weave merge %O %A %B %P"]) + execGit(tempDir, ["config", "--local", "merge.weave.name", "Weave semantic merge driver"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.ts merge=weave\n") + + const status = await service.getStatus(tempDir) + expect(status.isConfiguredInGitConfig).toBe(true) + expect(status.isConfiguredInGitattributes).toBe(true) + expect(status.isFullyConfigured).toBe(true) + }) + }) + + describe("configureGitConfig", () => { + it("should add merge.weave entries to local git config", async () => { + execGit(tempDir, ["init"]) + execGit(tempDir, ["config", "user.email", "test@test.com"]) + execGit(tempDir, ["config", "user.name", "Test"]) + + await service.configureGitConfig(tempDir) + + const result = await service.isConfiguredInGitConfig(tempDir) + expect(result).toBe(true) + }) + }) + + describe("configureGitattributes", () => { + it("should create .gitattributes when it does not exist", async () => { + execGit(tempDir, ["init"]) + + const added = await service.configureGitattributes(tempDir, ["*.py", "*.ts"]) + expect(added).toEqual(["*.py", "*.ts"]) + + const content = await fs.readFile(path.join(tempDir, ".gitattributes"), "utf-8") + expect(content).toContain("*.py merge=weave") + expect(content).toContain("*.ts merge=weave") + expect(content).toContain("# Weave semantic merge driver") + }) + + it("should append to existing .gitattributes", async () => { + execGit(tempDir, ["init"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.txt text\n") + + const added = await service.configureGitattributes(tempDir, ["*.py"]) + expect(added).toEqual(["*.py"]) + + const content = await fs.readFile(path.join(tempDir, ".gitattributes"), "utf-8") + expect(content).toContain("*.txt text") + expect(content).toContain("*.py merge=weave") + }) + + it("should not duplicate existing weave entries", async () => { + execGit(tempDir, ["init"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.py merge=weave\n") + + const added = await service.configureGitattributes(tempDir, ["*.py", "*.ts"]) + expect(added).toEqual(["*.ts"]) + + const content = await fs.readFile(path.join(tempDir, ".gitattributes"), "utf-8") + // Should only have one *.py line + const pyMatches = content.match(/\*\.py merge=weave/g) + expect(pyMatches?.length).toBe(1) + }) + + it("should return empty array when all entries already exist", async () => { + execGit(tempDir, ["init"]) + await fs.writeFile(path.join(tempDir, ".gitattributes"), "*.py merge=weave\n*.ts merge=weave\n") + + const added = await service.configureGitattributes(tempDir, ["*.py", "*.ts"]) + expect(added).toEqual([]) + }) + + it("should throw for non-git directory", async () => { + await expect(service.configureGitattributes(tempDir)).rejects.toThrow("Not a git repository") + }) + }) + + describe("configure", () => { + it("should return failure when weave is not installed", async () => { + execGit(tempDir, ["init"]) + + // Override PATH to ensure weave isn't found + const originalPath = process.env.PATH + process.env.PATH = "" + try { + const result = await service.configure(tempDir) + expect(result.success).toBe(false) + expect(result.message).toContain("weave is not installed") + expect(result.addedExtensions).toEqual([]) + } finally { + process.env.PATH = originalPath + } + }) + }) + + describe("unconfigure", () => { + it("should remove weave config and gitattributes entries", async () => { + execGit(tempDir, ["init"]) + execGit(tempDir, ["config", "--local", "merge.weave.driver", "weave merge %O %A %B %P"]) + execGit(tempDir, ["config", "--local", "merge.weave.name", "Weave semantic merge driver"]) + await fs.writeFile( + path.join(tempDir, ".gitattributes"), + "*.txt text\n# Weave semantic merge driver\n*.py merge=weave\n*.ts merge=weave\n", + ) + + const result = await service.unconfigure(tempDir) + expect(result.success).toBe(true) + + // Check git config was removed + const isConfigured = await service.isConfiguredInGitConfig(tempDir) + expect(isConfigured).toBe(false) + + // Check gitattributes was cleaned + const content = await fs.readFile(path.join(tempDir, ".gitattributes"), "utf-8") + expect(content).toContain("*.txt text") + expect(content).not.toContain("merge=weave") + expect(content).not.toContain("# Weave semantic merge driver") + }) + + it("should succeed even when no weave config exists", async () => { + execGit(tempDir, ["init"]) + + const result = await service.unconfigure(tempDir) + expect(result.success).toBe(true) + }) + }) +}) diff --git a/packages/core/src/worktree/index.ts b/packages/core/src/worktree/index.ts index ae07ef4aaaa..14976e29d62 100644 --- a/packages/core/src/worktree/index.ts +++ b/packages/core/src/worktree/index.ts @@ -11,3 +11,10 @@ export * from "./types.js" // Services export { WorktreeService, worktreeService } from "./worktree-service.js" export { WorktreeIncludeService, worktreeIncludeService, type CopyProgressCallback } from "./worktree-include.js" +export { + WeaveMergeDriverService, + weaveMergeDriverService, + WEAVE_SUPPORTED_EXTENSIONS, + buildGitattributesLines, + type WeaveMergeDriverStatus, +} from "./weave-merge-driver.js" diff --git a/packages/core/src/worktree/weave-merge-driver.ts b/packages/core/src/worktree/weave-merge-driver.ts new file mode 100644 index 00000000000..49f6655f60f --- /dev/null +++ b/packages/core/src/worktree/weave-merge-driver.ts @@ -0,0 +1,312 @@ +/** + * WeaveMergeDriverService + * + * Platform-agnostic service for detecting and configuring the weave semantic merge driver. + * Weave uses tree-sitter to merge at the function/class level, reducing false conflicts + * when parallel worktree tasks edit different functions in the same file. + * + * @see https://github.com/ataraxy-labs/weave + */ + +import { exec } from "child_process" +import * as fs from "fs/promises" +import * as path from "path" +import { promisify } from "util" + +const execAsync = promisify(exec) + +/** + * Supported file extensions for the weave merge driver. + * These correspond to languages that weave/tree-sitter can parse semantically. + */ +export const WEAVE_SUPPORTED_EXTENSIONS = [ + "*.py", + "*.js", + "*.jsx", + "*.ts", + "*.tsx", + "*.rs", + "*.go", + "*.java", + "*.c", + "*.cpp", + "*.h", + "*.hpp", + "*.rb", + "*.swift", + "*.kt", + "*.scala", + "*.cs", +] + +/** + * The gitattributes lines that configure weave as the merge driver for supported file types. + */ +export function buildGitattributesLines(extensions: string[] = WEAVE_SUPPORTED_EXTENSIONS): string[] { + return extensions.map((ext) => `${ext} merge=weave`) +} + +/** + * Status of the weave merge driver in a repository. + */ +export interface WeaveMergeDriverStatus { + /** Whether the weave binary is found on PATH */ + isInstalled: boolean + /** Version string if installed, undefined otherwise */ + version?: string + /** Whether the merge driver is configured in the repo's git config */ + isConfiguredInGitConfig: boolean + /** Whether .gitattributes contains weave merge driver entries */ + isConfiguredInGitattributes: boolean + /** Whether setup is fully complete (both git config and gitattributes) */ + isFullyConfigured: boolean +} + +/** + * Service for managing the weave semantic merge driver configuration. + * All methods are platform-agnostic and don't depend on VSCode APIs. + */ +export class WeaveMergeDriverService { + /** + * Check if weave is installed and available on PATH. + */ + async isInstalled(): Promise { + try { + await execAsync("weave --version") + return true + } catch { + return false + } + } + + /** + * Get the weave version string. + */ + async getVersion(): Promise { + try { + const { stdout } = await execAsync("weave --version") + return stdout.trim() + } catch { + return undefined + } + } + + /** + * Check if the weave merge driver is configured in the repo's local git config. + */ + async isConfiguredInGitConfig(cwd: string): Promise { + try { + const { stdout } = await execAsync("git config --local merge.weave.driver", { cwd }) + return stdout.trim().length > 0 + } catch { + return false + } + } + + /** + * Check if .gitattributes contains weave merge driver entries. + */ + async isConfiguredInGitattributes(cwd: string): Promise { + try { + const gitRoot = await this.getGitRoot(cwd) + if (!gitRoot) return false + + const gitattributesPath = path.join(gitRoot, ".gitattributes") + const content = await fs.readFile(gitattributesPath, "utf-8") + return content.includes("merge=weave") + } catch { + return false + } + } + + /** + * Get the full status of the weave merge driver for a repository. + */ + async getStatus(cwd: string): Promise { + const [isInstalled, version, isConfiguredInGitConfig, isConfiguredInGitattributes] = await Promise.all([ + this.isInstalled(), + this.getVersion(), + this.isConfiguredInGitConfig(cwd), + this.isConfiguredInGitattributes(cwd), + ]) + + return { + isInstalled, + version, + isConfiguredInGitConfig, + isConfiguredInGitattributes, + isFullyConfigured: isConfiguredInGitConfig && isConfiguredInGitattributes, + } + } + + /** + * Configure the weave merge driver in the repo's local git config. + * Adds: + * [merge "weave"] + * name = Weave semantic merge driver + * driver = weave merge %O %A %B %P + */ + async configureGitConfig(cwd: string): Promise { + await execAsync('git config --local merge.weave.name "Weave semantic merge driver"', { cwd }) + await execAsync('git config --local merge.weave.driver "weave merge %O %A %B %P"', { cwd }) + } + + /** + * Add weave merge driver entries to .gitattributes. + * If .gitattributes already exists, appends the entries (avoiding duplicates). + * If it doesn't exist, creates it. + * + * @param cwd - Current working directory (must be in a git repo) + * @param extensions - File extensions to configure (defaults to WEAVE_SUPPORTED_EXTENSIONS) + * @returns The list of extensions that were actually added (excludes already-configured ones) + */ + async configureGitattributes(cwd: string, extensions: string[] = WEAVE_SUPPORTED_EXTENSIONS): Promise { + const gitRoot = await this.getGitRoot(cwd) + if (!gitRoot) { + throw new Error("Not a git repository") + } + + const gitattributesPath = path.join(gitRoot, ".gitattributes") + let existingContent = "" + + try { + existingContent = await fs.readFile(gitattributesPath, "utf-8") + } catch { + // File doesn't exist yet, that's fine + } + + const existingLines = new Set(existingContent.split("\n").map((line) => line.trim())) + const newLines = buildGitattributesLines(extensions) + const addedExtensions: string[] = [] + + const linesToAdd: string[] = [] + for (let i = 0; i < newLines.length; i++) { + const line = newLines[i]! + const ext = extensions[i]! + if (!existingLines.has(line)) { + linesToAdd.push(line) + addedExtensions.push(ext) + } + } + + if (linesToAdd.length > 0) { + const separator = + existingContent.length > 0 && !existingContent.endsWith("\n") + ? "\n\n# Weave semantic merge driver\n" + : existingContent.length > 0 + ? "\n# Weave semantic merge driver\n" + : "# Weave semantic merge driver\n" + + const newContent = existingContent + separator + linesToAdd.join("\n") + "\n" + await fs.writeFile(gitattributesPath, newContent, "utf-8") + } + + return addedExtensions + } + + /** + * Fully configure the weave merge driver for a repository. + * Sets up both the git config and .gitattributes entries. + * + * @param cwd - Current working directory (must be in a git repo) + * @param extensions - File extensions to configure (defaults to WEAVE_SUPPORTED_EXTENSIONS) + * @returns Status after configuration + */ + async configure( + cwd: string, + extensions: string[] = WEAVE_SUPPORTED_EXTENSIONS, + ): Promise<{ success: boolean; message: string; addedExtensions: string[] }> { + const isInstalled = await this.isInstalled() + if (!isInstalled) { + return { + success: false, + message: + "weave is not installed. Install it with: brew install ataraxy-labs/tap/weave (macOS) or cargo install weave-merge (other platforms). See https://github.com/ataraxy-labs/weave", + addedExtensions: [], + } + } + + try { + await this.configureGitConfig(cwd) + const addedExtensions = await this.configureGitattributes(cwd, extensions) + + const extMsg = + addedExtensions.length > 0 + ? `Added ${addedExtensions.length} file pattern(s) to .gitattributes.` + : ".gitattributes already configured." + + return { + success: true, + message: `Weave semantic merge driver configured. ${extMsg}`, + addedExtensions, + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { + success: false, + message: `Failed to configure weave merge driver: ${errorMessage}`, + addedExtensions: [], + } + } + } + + /** + * Remove weave merge driver configuration from a repository. + * Removes the git config entries and weave lines from .gitattributes. + */ + async unconfigure(cwd: string): Promise<{ success: boolean; message: string }> { + try { + // Remove git config entries + try { + await execAsync("git config --local --remove-section merge.weave", { cwd }) + } catch { + // Section may not exist + } + + // Remove weave lines from .gitattributes + const gitRoot = await this.getGitRoot(cwd) + if (gitRoot) { + const gitattributesPath = path.join(gitRoot, ".gitattributes") + try { + const content = await fs.readFile(gitattributesPath, "utf-8") + const lines = content.split("\n") + const filteredLines = lines.filter( + (line) => !line.includes("merge=weave") && line.trim() !== "# Weave semantic merge driver", + ) + + // Clean up double blank lines + const cleanedContent = filteredLines.join("\n").replace(/\n{3,}/g, "\n\n") + await fs.writeFile(gitattributesPath, cleanedContent, "utf-8") + } catch { + // .gitattributes may not exist + } + } + + return { + success: true, + message: "Weave merge driver configuration removed.", + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { + success: false, + message: `Failed to remove weave configuration: ${errorMessage}`, + } + } + } + + /** + * Get the git repository root path. + */ + private async getGitRoot(cwd: string): Promise { + try { + const { stdout } = await execAsync("git rev-parse --show-toplevel", { cwd }) + return stdout.trim() + } catch { + return null + } + } +} + +// Export singleton instance for convenience +export const weaveMergeDriverService = new WeaveMergeDriverService() diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 15edd13db45..3ce2fe4a989 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -101,6 +101,8 @@ export interface ExtensionMessage { | "worktreeDefaults" | "worktreeIncludeStatus" | "branchWorktreeIncludeResult" + | "weaveMergeDriverStatus" + | "weaveMergeDriverResult" | "folderSelected" | "skills" | "fileContent" @@ -236,6 +238,14 @@ export interface ExtensionMessage { copyProgressBytesCopied?: number copyProgressTotalBytes?: number copyProgressItemName?: string + // Weave merge driver status + weaveMergeDriverStatus?: { + isInstalled: boolean + version?: string + isConfiguredInGitConfig: boolean + isConfiguredInGitattributes: boolean + isFullyConfigured: boolean + } // folderSelected path?: string } @@ -572,6 +582,9 @@ export interface WebviewMessage { | "createWorktreeInclude" | "checkoutBranch" | "browseForWorktreePath" + | "getWeaveMergeDriverStatus" + | "configureWeaveMergeDriver" + | "unconfigureWeaveMergeDriver" // Skills messages | "requestSkills" | "createSkill" diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 5194b16df9d..62c0124bab1 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -86,6 +86,9 @@ import { handleCheckBranchWorktreeInclude, handleCreateWorktreeInclude, handleCheckoutBranch, + handleGetWeaveMergeDriverStatus, + handleConfigureWeaveMergeDriver, + handleUnconfigureWeaveMergeDriver, } from "./worktree" export const webviewMessageHandler = async ( @@ -3555,6 +3558,59 @@ export const webviewMessageHandler = async ( break } + case "getWeaveMergeDriverStatus": { + try { + const weaveMergeDriverStatus = await handleGetWeaveMergeDriverStatus(provider) + await provider.postMessageToWebview({ type: "weaveMergeDriverStatus", weaveMergeDriverStatus }) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + provider.log(`Error getting weave merge driver status: ${errorMessage}`) + await provider.postMessageToWebview({ + type: "weaveMergeDriverStatus", + weaveMergeDriverStatus: { + isInstalled: false, + isConfiguredInGitConfig: false, + isConfiguredInGitattributes: false, + isFullyConfigured: false, + }, + }) + } + + break + } + + case "configureWeaveMergeDriver": { + try { + const { success, message: text } = await handleConfigureWeaveMergeDriver(provider) + await provider.postMessageToWebview({ type: "weaveMergeDriverResult", success, text }) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + await provider.postMessageToWebview({ + type: "weaveMergeDriverResult", + success: false, + text: errorMessage, + }) + } + + break + } + + case "unconfigureWeaveMergeDriver": { + try { + const { success, message: text } = await handleUnconfigureWeaveMergeDriver(provider) + await provider.postMessageToWebview({ type: "weaveMergeDriverResult", success, text }) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + await provider.postMessageToWebview({ + type: "weaveMergeDriverResult", + success: false, + text: errorMessage, + }) + } + + break + } + case "browseForWorktreePath": { try { const options: vscode.OpenDialogOptions = { diff --git a/src/core/webview/worktree/handlers.ts b/src/core/webview/worktree/handlers.ts index 67c88b910ee..72454efbc8c 100644 --- a/src/core/webview/worktree/handlers.ts +++ b/src/core/webview/worktree/handlers.ts @@ -16,7 +16,13 @@ import type { WorktreeListResponse, WorktreeDefaultsResponse, } from "@roo-code/types" -import { worktreeService, worktreeIncludeService, type CopyProgressCallback } from "@roo-code/core" +import { + worktreeService, + worktreeIncludeService, + weaveMergeDriverService, + type CopyProgressCallback, +} from "@roo-code/core" +import type { WeaveMergeDriverStatus } from "@roo-code/core" import type { ClineProvider } from "../ClineProvider" @@ -277,3 +283,22 @@ export async function handleCheckoutBranch(provider: ClineProvider, branch: stri const cwd = provider.cwd return worktreeService.checkoutBranch(cwd, branch) } + +export async function handleGetWeaveMergeDriverStatus(provider: ClineProvider): Promise { + const cwd = provider.cwd + return weaveMergeDriverService.getStatus(cwd) +} + +export async function handleConfigureWeaveMergeDriver(provider: ClineProvider): Promise { + const cwd = provider.cwd + const result = await weaveMergeDriverService.configure(cwd) + return { + success: result.success, + message: result.message, + } +} + +export async function handleUnconfigureWeaveMergeDriver(provider: ClineProvider): Promise { + const cwd = provider.cwd + return weaveMergeDriverService.unconfigure(cwd) +} diff --git a/src/core/webview/worktree/index.ts b/src/core/webview/worktree/index.ts index b8631860e08..2794ef311aa 100644 --- a/src/core/webview/worktree/index.ts +++ b/src/core/webview/worktree/index.ts @@ -16,6 +16,9 @@ export { handleCheckBranchWorktreeInclude, handleCreateWorktreeInclude, handleCheckoutBranch, + handleGetWeaveMergeDriverStatus, + handleConfigureWeaveMergeDriver, + handleUnconfigureWeaveMergeDriver, } from "./handlers" // Re-export types from @roo-code/types for convenience