Skip to content

Commit 75a5057

Browse files
committed
feat(code): enable branch linking for local tasks
1 parent d95d46a commit 75a5057

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed

apps/code/src/main/services/workspace/service.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
33
import * as fsPromises from "node:fs/promises";
44
import path from "node:path";
55
import { promisify } from "node:util";
6+
import { trackAppEvent } from "@main/services/posthog-analytics";
67
import { createGitClient } from "@posthog/git/client";
78
import {
89
getCurrentBranch,
@@ -22,6 +23,7 @@ import { MAIN_TOKENS } from "../../di/tokens";
2223
import { logger } from "../../utils/logger";
2324
import { TypedEventEmitter } from "../../utils/typed-event-emitter";
2425
import { deriveWorktreePath } from "../../utils/worktree-helpers";
26+
import { AgentServiceEvent } from "../agent/schemas";
2527
import type { AgentService } from "../agent/service";
2628
import { FileWatcherEvent } from "../file-watcher/schemas";
2729
import type { FileWatcherService } from "../file-watcher/service";
@@ -237,6 +239,11 @@ export class WorkspaceService extends TypedEventEmitter<WorkspaceServiceEvents>
237239
this.handleFocusBranchRenamed.bind(this),
238240
);
239241

242+
this.agentService.on(
243+
AgentServiceEvent.AgentFileActivity,
244+
this.handleAgentFileActivity.bind(this),
245+
);
246+
240247
log.info("Branch watcher initialized");
241248
}
242249

@@ -310,27 +317,75 @@ export class WorkspaceService extends TypedEventEmitter<WorkspaceServiceEvents>
310317
}
311318
}
312319

320+
private async handleAgentFileActivity({
321+
taskId,
322+
branchName,
323+
}: {
324+
taskId: string;
325+
branchName: string | null;
326+
}): Promise<void> {
327+
if (!branchName) return;
328+
329+
const dbRow = this.workspaceRepo.findByTaskId(taskId);
330+
if (!dbRow || dbRow.mode !== "local") return;
331+
if (!dbRow.repositoryId) return;
332+
333+
const folderPath = this.getFolderPath(dbRow.repositoryId);
334+
if (!folderPath) return;
335+
336+
try {
337+
const defaultBranch = await getDefaultBranch(folderPath);
338+
if (branchName === defaultBranch) return;
339+
} catch (error) {
340+
log.warn("Failed to determine default branch, skipping branch link", {
341+
taskId,
342+
branchName,
343+
error,
344+
});
345+
trackAppEvent("branch_link_default_branch_unknown", {
346+
taskId,
347+
branchName,
348+
});
349+
return;
350+
}
351+
352+
const currentLinked = dbRow.linkedBranch ?? null;
353+
if (currentLinked === branchName) return;
354+
355+
this.linkBranch(taskId, branchName, "agent");
356+
}
357+
313358
private updateAssociationBranchName(
314359
_taskId: string,
315360
_branchName: string,
316361
): void {}
317362

318-
public linkBranch(taskId: string, branchName: string): void {
363+
public linkBranch(
364+
taskId: string,
365+
branchName: string,
366+
source?: "agent" | "user",
367+
): void {
319368
this.workspaceRepo.updateLinkedBranch(taskId, branchName);
320369
this.emit(WorkspaceServiceEvent.LinkedBranchChanged, {
321370
taskId,
322371
branchName,
323372
});
324-
log.info("Linked branch to task", { taskId, branchName });
373+
trackAppEvent("branch_linked", {
374+
source: source ?? "unknown",
375+
});
376+
log.info("Linked branch to task", { taskId, branchName, source });
325377
}
326378

327-
public unlinkBranch(taskId: string): void {
379+
public unlinkBranch(taskId: string, source?: "agent" | "user"): void {
328380
this.workspaceRepo.updateLinkedBranch(taskId, null);
329381
this.emit(WorkspaceServiceEvent.LinkedBranchChanged, {
330382
taskId,
331383
branchName: null,
332384
});
333-
log.info("Unlinked branch from task", { taskId });
385+
trackAppEvent("branch_unlinked", {
386+
source: source ?? "unknown",
387+
});
388+
log.info("Unlinked branch from task", { taskId, source });
334389
}
335390

336391
private async getLocalWorktreePathIfExists(

apps/code/src/main/trpc/routers/workspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,12 @@ export const workspaceRouter = router({
186186
linkBranch: publicProcedure
187187
.input(linkBranchInput)
188188
.mutation(({ input }) =>
189-
getService().linkBranch(input.taskId, input.branchName),
189+
getService().linkBranch(input.taskId, input.branchName, "user"),
190190
),
191191

192192
unlinkBranch: publicProcedure
193193
.input(unlinkBranchInput)
194-
.mutation(({ input }) => getService().unlinkBranch(input.taskId)),
194+
.mutation(({ input }) => getService().unlinkBranch(input.taskId, "user")),
195195

196196
onError: subscribe(WorkspaceServiceEvent.Error),
197197
onWarning: subscribe(WorkspaceServiceEvent.Warning),

apps/code/src/renderer/features/git-interaction/hooks/useGitInteraction.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,17 @@ export function useGitInteraction(
276276
}
277277
if (store.createPrNeedsBranch) {
278278
invalidateGitBranchQueries(repoPath);
279+
trpcClient.workspace.linkBranch
280+
.mutate({ taskId, branchName: store.branchName.trim() })
281+
.catch((err) =>
282+
log.warn("Failed to link branch to task", { taskId, err }),
283+
);
284+
} else if (git.currentBranch) {
285+
trpcClient.workspace.linkBranch
286+
.mutate({ taskId, branchName: git.currentBranch })
287+
.catch((err) =>
288+
log.warn("Failed to link branch to task", { taskId, err }),
289+
);
279290
}
280291

281292
if (result.prUrl) {
@@ -529,6 +540,12 @@ export function useGitInteraction(
529540
trackGitAction(taskId, "branch-here", true);
530541
await queryClient.invalidateQueries(trpc.workspace.getAll.pathFilter());
531542

543+
trpcClient.workspace.linkBranch
544+
.mutate({ taskId, branchName: store.branchName.trim() })
545+
.catch((err) =>
546+
log.warn("Failed to link branch to task", { taskId, err }),
547+
);
548+
532549
modal.closeBranch();
533550
} catch (error) {
534551
log.error("Failed to create branch", error);

0 commit comments

Comments
 (0)