-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add per-project worktree mode setting #2533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
e7a3779
1cf4a0f
6867bd2
60c9f87
5c8d783
f878bcd
797c226
6f86de9
8dfa3ce
5b9a80f
3ecf401
93a5305
2784f60
48da360
c4a0324
3e531b8
7b18481
d14d19d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { | |
| projects, | ||
| type SelectProject, | ||
| settings, | ||
| WORKTREE_MODES, | ||
| workspaceSections, | ||
| workspaces, | ||
| worktrees, | ||
|
|
@@ -1230,6 +1231,7 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => { | |
| worktreeBaseDir: z.string().nullable().optional(), | ||
| hideImage: z.boolean().optional(), | ||
| defaultApp: z.enum(EXTERNAL_APPS).nullable().optional(), | ||
| worktreeMode: z.enum(WORKTREE_MODES).nullable().optional(), | ||
| }), | ||
| }), | ||
| ) | ||
|
|
@@ -1268,6 +1270,9 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => { | |
| ...(input.patch.defaultApp !== undefined && { | ||
| defaultApp: input.patch.defaultApp, | ||
| }), | ||
| ...(input.patch.worktreeMode !== undefined && { | ||
| worktreeMode: input.patch.worktreeMode, | ||
| }), | ||
| lastOpenedAt: Date.now(), | ||
| }) | ||
| .where(eq(projects.id, input.id)) | ||
|
|
@@ -1366,7 +1371,12 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => { | |
| }), | ||
|
|
||
| close: publicProcedure | ||
| .input(z.object({ id: z.string() })) | ||
| .input( | ||
| z.object({ | ||
| id: z.string(), | ||
| deleteWorktrees: z.boolean().optional().default(false), | ||
| }), | ||
| ) | ||
| .mutation(async ({ input }) => { | ||
| const project = localDb | ||
| .select() | ||
|
|
@@ -1394,6 +1404,56 @@ export const createProjectsRouter = (getWindow: () => BrowserWindow | null) => { | |
|
|
||
| const closedWorkspaceIds = projectWorkspaces.map((w) => w.id); | ||
|
|
||
| // Optionally move worktree directories to Trash | ||
| if (input.deleteWorktrees) { | ||
| const { existsSync } = await import("node:fs"); | ||
| const { shell } = await import("electron"); | ||
|
|
||
| // Collect worktree paths from both the worktrees table and workspace records | ||
| const projectWorktrees = localDb | ||
| .select() | ||
| .from(worktrees) | ||
| .where(eq(worktrees.projectId, input.id)) | ||
| .all(); | ||
|
|
||
| const worktreePaths = new Set<string>( | ||
| projectWorktrees.map((wt) => wt.path), | ||
| ); | ||
|
|
||
| for (const ws of projectWorkspaces) { | ||
| if (ws.type === "worktree" && ws.worktreeId) { | ||
| const wt = localDb | ||
| .select() | ||
| .from(worktrees) | ||
| .where(eq(worktrees.id, ws.worktreeId)) | ||
| .get(); | ||
| if (wt?.path) { | ||
| worktreePaths.add(wt.path); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (const wtPath of worktreePaths) { | ||
| if (!existsSync(wtPath)) continue; | ||
| try { | ||
| await shell.trashItem(wtPath); | ||
| } catch (error) { | ||
| console.error( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Worktree trash failures are swallowed, but project/worktree metadata is still deleted and the mutation returns success, causing silent partial cleanup and potential orphaned directories. Prompt for AI agents |
||
| `[projects/close] Failed to trash worktree ${wtPath}:`, | ||
| error, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Clean up stale git worktree references | ||
| try { | ||
| const git = await getSimpleGitWithShellPath(project.mainRepoPath); | ||
| await git.raw(["worktree", "prune"]); | ||
| } catch (error) { | ||
| console.error("[projects/close] Failed to prune worktrees:", error); | ||
| } | ||
| } | ||
|
|
||
| if (closedWorkspaceIds.length > 0) { | ||
| localDb | ||
| .delete(workspaces) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -295,6 +295,7 @@ export const createCreateProcedures = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| baseBranch: z.string().optional(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useExistingBranch: z.boolean().optional(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| applyPrefix: z.boolean().optional().default(true), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useWorktree: z.boolean().optional(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .mutation(async ({ input }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -307,6 +308,87 @@ export const createCreateProcedures = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Project ${input.projectId} not found`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Resolve effective worktree mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const globalSettings = localDb.select().from(settings).get(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const effectiveWorktreeMode = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project.worktreeMode ?? globalSettings?.worktreeMode ?? "always"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If worktrees are disabled (or user explicitly chose no worktree), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // open directly in the main repo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| effectiveWorktreeMode === "disabled" || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.useWorktree === false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const branch = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input.branchName?.trim() || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (await getCurrentBranch(project.mainRepoPath)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!branch) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Could not determine current branch"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const existing = getBranchWorkspace(input.projectId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (existing) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (existing.branch !== branch) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| localDb | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .update(workspaces) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .set({ branch }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .where(eq(workspaces.id, existing.id)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .run(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| touchWorkspace(existing.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLastActiveWorkspace(existing.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...existing, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| branch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lastOpenedAt: Date.now(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| worktreePath: project.mainRepoPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projectId: project.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isInitializing: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wasExisting: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const maxTabOrder = getMaxProjectChildTabOrder(input.projectId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const workspace = localDb | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .insert(workspaces) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .values({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projectId: input.projectId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "branch", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| branch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: input.name ?? branch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tabOrder: maxTabOrder + 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .onConflictDoNothing() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .returning() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .all(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ws = workspace[0] ?? getBranchWorkspace(input.projectId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!ws) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Failed to create or find branch workspace"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLastActiveWorkspace(ws.id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| activateProject(project); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| track("workspace_created", { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace_id: ws.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project_id: project.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "branch", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace: ws, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initialCommands: null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| worktreePath: project.mainRepoPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| projectId: project.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isInitializing: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| wasExisting: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+358
to
+393
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve After Suggested adjustment- const workspace = localDb
+ const insertResult = localDb
.insert(workspaces)
.values({
projectId: input.projectId,
type: "branch",
branch,
name: input.name ?? branch,
tabOrder: maxTabOrder + 1,
})
.onConflictDoNothing()
.returning()
.all();
- const ws = workspace[0] ?? getBranchWorkspace(input.projectId);
+ const wasExisting = insertResult.length === 0;
+ const ws =
+ insertResult[0] ?? getBranchWorkspace(input.projectId);
if (!ws) {
throw new Error("Failed to create or find branch workspace");
}
@@
- track("workspace_created", {
- workspace_id: ws.id,
- project_id: project.id,
- type: "branch",
- });
+ if (!wasExisting) {
+ track("workspace_created", {
+ workspace_id: ws.id,
+ project_id: project.id,
+ type: "branch",
+ });
+ }
@@
- wasExisting: false,
+ wasExisting,
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let existingBranchName: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.useExistingBranch) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| existingBranchName = input.branchName?.trim(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -332,7 +414,6 @@ export const createCreateProcedures = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let branchPrefix: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.applyPrefix) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const globalSettings = localDb.select().from(settings).get(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const projectOverrides = project.branchPrefixMode != null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prefixMode = projectOverrides | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? project.branchPrefixMode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -154,6 +154,7 @@ export const createDeleteProcedures = () => { | |
| id: z.string(), | ||
| deleteLocalBranch: z.boolean().optional(), | ||
| force: z.boolean().optional(), | ||
| trash: z.boolean().optional(), | ||
| }), | ||
| ) | ||
| .mutation(async ({ input }) => { | ||
|
|
@@ -252,13 +253,28 @@ export const createDeleteProcedures = () => { | |
| await workspaceInitManager.acquireProjectLock(project.id); | ||
|
|
||
| try { | ||
| const removeResult = await removeWorktreeFromDisk({ | ||
| mainRepoPath: project.mainRepoPath, | ||
| worktreePath: worktree.path, | ||
| }); | ||
| if (!removeResult.success) { | ||
| clearWorkspaceDeletingStatus(input.id); | ||
| return removeResult; | ||
| if (input.trash) { | ||
| // Move to Trash (recoverable) instead of permanent delete | ||
| const { existsSync } = await import("node:fs"); | ||
| if (existsSync(worktree.path)) { | ||
| const { shell } = await import("electron"); | ||
| await shell.trashItem(worktree.path); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Trash-mode deletion lacks failure cleanup; thrown errors can leave Prompt for AI agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Trash-mode delete lacks failure cleanup, so thrown async errors can leave Prompt for AI agents |
||
| } | ||
| // Clean up stale git worktree references | ||
| const { getSimpleGitWithShellPath } = await import( | ||
| "../utils/git-client" | ||
| ); | ||
| const git = await getSimpleGitWithShellPath(project.mainRepoPath); | ||
| await git.raw(["worktree", "prune"]); | ||
|
Comment on lines
+256
to
+268
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant import and missing error handling in trash path.
Proposed fix if (input.trash) {
// Move to Trash (recoverable) instead of permanent delete
- const { existsSync } = await import("node:fs");
if (existsSync(worktree.path)) {
- const { shell } = await import("electron");
- await shell.trashItem(worktree.path);
+ try {
+ const { shell } = await import("electron");
+ await shell.trashItem(worktree.path);
+ } catch (error) {
+ console.error(
+ `[workspace/delete] Failed to move worktree to trash:`,
+ error instanceof Error ? error.message : String(error),
+ );
+ clearWorkspaceDeletingStatus(input.id);
+ return {
+ success: false,
+ error: `Failed to move worktree to trash: ${error instanceof Error ? error.message : String(error)}`,
+ };
+ }
}
// Clean up stale git worktree references
- const { getSimpleGitWithShellPath } = await import(
- "../utils/git-client"
- );
- const git = await getSimpleGitWithShellPath(project.mainRepoPath);
- await git.raw(["worktree", "prune"]);
+ try {
+ const { getSimpleGitWithShellPath } = await import(
+ "../utils/git-client"
+ );
+ const git = await getSimpleGitWithShellPath(project.mainRepoPath);
+ await git.raw(["worktree", "prune"]);
+ } catch (error) {
+ // Non-blocking: prune failure shouldn't prevent workspace deletion
+ console.warn(
+ `[workspace/delete] git worktree prune failed (non-blocking):`,
+ error instanceof Error ? error.message : String(error),
+ );
+ }
} else {🤖 Prompt for AI Agents
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| } else { | ||
| const removeResult = await removeWorktreeFromDisk({ | ||
| mainRepoPath: project.mainRepoPath, | ||
| worktreePath: worktree.path, | ||
| }); | ||
| if (!removeResult.success) { | ||
| clearWorkspaceDeletingStatus(input.id); | ||
| return removeResult; | ||
| } | ||
| } | ||
| } finally { | ||
| workspaceInitManager.releaseProjectLock(project.id); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -152,6 +152,7 @@ export const createQueryProcedures = () => { | |
| mainRepoPath: string; | ||
| hideImage: boolean; | ||
| iconUrl: string | null; | ||
| worktreeMode: string | null; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||
| }; | ||
| workspaces: WorkspaceItem[]; | ||
| sections: SectionItem[]; | ||
|
|
@@ -184,6 +185,7 @@ export const createQueryProcedures = () => { | |
| mainRepoPath: project.mainRepoPath, | ||
| hideImage: project.hideImage ?? false, | ||
| iconUrl: project.iconUrl ?? null, | ||
| worktreeMode: project.worktreeMode ?? null, | ||
| }, | ||
| workspaces: [], | ||
| sections: projectSections, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -84,7 +84,10 @@ export function NewWorkspaceModalContent({ | |||||
| <PromptGroup | ||||||
| projectId={draft.selectedProjectId} | ||||||
| selectedProject={selectedProject} | ||||||
| recentProjects={recentProjects.filter((project) => Boolean(project.id))} | ||||||
| recentProjects={recentProjects.filter( | ||||||
| (project) => | ||||||
| Boolean(project.id) && project.worktreeMode !== "disabled", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Filtering out Prompt for AI agents
Suggested change
|
||||||
| )} | ||||||
| onSelectProject={(selectedProjectId) => | ||||||
| updateDraft({ selectedProjectId }) | ||||||
| } | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -471,6 +471,11 @@ function PromptGroupInner({ | |
| const { data: globalBranchPrefix } = | ||
| electronTrpc.settings.getBranchPrefix.useQuery(); | ||
| const { data: gitInfo } = electronTrpc.settings.getGitInfo.useQuery(); | ||
| const { data: globalWorktreeMode } = | ||
| electronTrpc.settings.getWorktreeMode.useQuery(); | ||
|
|
||
| const effectiveWorktreeMode = | ||
| project?.worktreeMode ?? globalWorktreeMode ?? "always"; | ||
|
|
||
| const resolvedPrefix = useMemo(() => { | ||
| const projectOverrides = project?.branchPrefixMode != null; | ||
|
|
@@ -637,6 +642,9 @@ function PromptGroupInner({ | |
| ? sanitizeBranchNameWithMaxLength(branchName.trim()) | ||
| : branchSlug) || undefined, | ||
| baseBranch: baseBranch || undefined, | ||
| ...(effectiveWorktreeMode === "disabled" && { | ||
| useWorktree: false, | ||
| }), | ||
|
Comment on lines
+719
to
+721
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if the "optional" mode toggle UI is implemented elsewhere in NewWorkspaceModal
rg -n "optional|useWorktree|worktreeMode" --type=tsx apps/desktop/src/renderer/components/NewWorkspaceModal/ -C 3
# Also check for any toggle component that might be conditionally rendered
ast-grep --pattern 'effectiveWorktreeMode === "optional"'Repository: superset-sh/superset Length of output: 91 🏁 Script executed: #!/bin/bash
# Check if the "optional" mode toggle UI is implemented
find apps/desktop/src/renderer/components/NewWorkspaceModal -name "*.tsx" -o -name "*.ts" | head -20Repository: superset-sh/superset Length of output: 1429 🏁 Script executed: #!/bin/bash
# Search for worktreeMode and optional handling in the files
rg -n "optional|useWorktree|worktreeMode" apps/desktop/src/renderer/components/NewWorkspaceModal/ -C 3Repository: superset-sh/superset Length of output: 4883 🏁 Script executed: #!/bin/bash
# Read the PromptGroup.tsx file to see the context around lines 645-647
wc -l apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsxRepository: superset-sh/superset Length of output: 161 🏁 Script executed: #!/bin/bash
# Read the context around lines 645-647 in PromptGroup.tsx
sed -n '630,670p' apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx | cat -nRepository: superset-sh/superset Length of output: 1388 🏁 Script executed: #!/bin/bash
# Search for any state related to worktree toggle or useWorktree variable assignments
rg -n "useState|useWorktree.*=" apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx | head -30Repository: superset-sh/superset Length of output: 429 🏁 Script executed: #!/bin/bash
# Check if there's a toggle component for worktree mode selection
rg -n "Toggle|Checkbox|Switch|optional" apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsxRepository: superset-sh/superset Length of output: 170 🏁 Script executed: #!/bin/bash
# Check the PromptGroupAdvancedOptions component which might have the worktree toggle
cat -n apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/components/PromptGroupAdvancedOptions/PromptGroupAdvancedOptions.tsxRepository: superset-sh/superset Length of output: 8443 🏁 Script executed: #!/bin/bash
# Check for any "optional" worktree mode handling throughout the entire NewWorkspaceModal
rg -n "optional" apps/desktop/src/renderer/components/NewWorkspaceModal/ -A 5 -B 5Repository: superset-sh/superset Length of output: 2991 🏁 Script executed: #!/bin/bash
# Check the full useState declarations to understand what state is tracked
sed -n '40,500p' apps/desktop/src/renderer/components/NewWorkspaceModal/components/PromptGroup/PromptGroup.tsx | grep -n "useState\|const \[" | head -20Repository: superset-sh/superset Length of output: 428 🏁 Script executed: #!/bin/bash
# Check the draft context to see if worktree choice is stored there
cat -n apps/desktop/src/renderer/components/NewWorkspaceModal/NewWorkspaceModalDraftContext.tsx | head -100Repository: superset-sh/superset Length of output: 3420 🏁 Script executed: #!/antml:bin/bash
# Search for the mutation definition to understand what useWorktree expects
rg -n "createWorkspace|useWorktree" apps/desktop/src/renderer/components/NewWorkspaceModal/ -B 3 -A 3Repository: superset-sh/superset Length of output: 8755 🏁 Script executed: #!/bin/bash
# Check if there's any documentation or PR info about worktree modes
rg -n "worktreeMode.*optional|optional.*worktree" --type=md --type=txt apps/desktop/Repository: superset-sh/superset Length of output: 398 🏁 Script executed: #!/bin/bash
# Search for the useCreateWorkspace mutation definition to understand what happens when useWorktree is not passed
fd -t f "useCreateWorkspace" apps/desktop/src/renderer/react-query/workspaces/ | head -5Repository: superset-sh/superset Length of output: 134 🏁 Script executed: #!/bin/bash
# Read the useCreateWorkspace hook to understand the mutation schema
cat apps/desktop/src/renderer/react-query/workspaces/useCreateWorkspace.tsRepository: superset-sh/superset Length of output: 4905 🏁 Script executed: #!/bin/bash
# Check if there are any tests or comments about worktree mode "optional" behavior
rg -n "optional" apps/desktop/src/renderer/components/NewWorkspaceModal/ -B 2 -A 2Repository: superset-sh/superset Length of output: 1442 🏁 Script executed: #!/bin/bash
# Find the tRPC workspaces router to understand the mutation schema
find apps/desktop -name "*.ts" -o -name "*.tsx" | xargs grep -l "workspaces.*create.*procedure\|workspaces.*create.*router" | head -5Repository: superset-sh/superset Length of output: 46 🏁 Script executed: #!/bin/bash
# Search for worktree-related code in the backend/shared types
rg -n "worktreeMode.*optional\|optional.*worktree" apps/desktop/ --type=ts --type=tsx -B 5 -A 5Repository: superset-sh/superset Length of output: 91 🏁 Script executed: #!/antml/bash
# Check the trpc-electron router definition
find apps -name "*router*" -o -name "*trpc*" | grep -E "workspaces|create" | head -10Repository: superset-sh/superset Length of output: 46 🏁 Script executed: #!/bin/bash
# Search for where the workspaces.create mutation is defined
rg -n "workspaces\.create\|\.create\(" apps/desktop/src --type ts | grep -i router | head -10Repository: superset-sh/superset Length of output: 46 🏁 Script executed: #!/bin/bash
# Look for trpc router or workspace routes
find apps/desktop -type f -name "*.ts" | xargs grep -l "workspaces.*create" | head -10Repository: superset-sh/superset Length of output: 740 🏁 Script executed: #!/bin/bash
# Search for the electron trpc definition
rg -n "electronTrpc\|trpc-electron" apps/desktop/src -l | head -10Repository: superset-sh/superset Length of output: 46 🏁 Script executed: #!/bin/bash
# Read the create procedure to understand what parameters it accepts
cat apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.tsRepository: superset-sh/superset Length of output: 29567 🏁 Script executed: #!/bin/bash
# Search for any recent changes or TODOs related to optional worktree mode
rg -n "TODO|FIXME|optional.*worktree\|worktree.*optional" apps/desktop/src/renderer/components/NewWorkspaceModal/ -B 2 -A 2Repository: superset-sh/superset Length of output: 46 🏁 Script executed: #!/bin/bash
# Check if there's an "always" mode to understand the pattern
rg -n "always\|optional\|disabled" apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts | head -20Repository: superset-sh/superset Length of output: 46 Add worktree toggle UI for "optional" mode. When Implement:
🤖 Prompt for AI Agents |
||
| }, | ||
| { | ||
| agentLaunchRequest: launchRequest ?? undefined, | ||
|
|
@@ -661,6 +669,7 @@ function PromptGroupInner({ | |
| convertBlobUrlToDataUrl, | ||
| createFromPr, | ||
| createWorkspace, | ||
| effectiveWorktreeMode, | ||
| linkedPR, | ||
| projectId, | ||
| runAsyncAction, | ||
|
|
@@ -791,7 +800,7 @@ function PromptGroupInner({ | |
| )} | ||
| <PromptInputTextarea | ||
| autoFocus | ||
| placeholder="What do you want to do?" | ||
| placeholder="What do you want to do? (optional)" | ||
| className="min-h-10" | ||
| value={prompt} | ||
| onChange={(e) => updateDraft({ prompt: e.target.value })} | ||
|
|
@@ -903,9 +912,11 @@ function PromptGroupInner({ | |
| )} | ||
| </AnimatePresence> | ||
| </div> | ||
| <span className="text-[11px] text-muted-foreground/50"> | ||
| {modKey}+↵ to create | ||
| </span> | ||
| <div className="flex items-center gap-2"> | ||
| <span className="text-[11px] text-muted-foreground/50"> | ||
| {modKey}+↵ to create | ||
| </span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { useWorktreeChoiceDialogStore } from "renderer/stores/worktree-choice-dialog"; | ||
| import { WorktreeChoiceDialog } from "./WorktreeChoiceDialog"; | ||
|
|
||
| export function ConnectedWorktreeChoiceDialog() { | ||
| const { isOpen, projectName, onChoice, close } = | ||
| useWorktreeChoiceDialogStore(); | ||
|
|
||
| return ( | ||
| <WorktreeChoiceDialog | ||
| projectName={projectName} | ||
| open={isOpen} | ||
| onOpenChange={(open) => { | ||
| if (!open) close(); | ||
| }} | ||
| onChoice={(enableWorktrees) => { | ||
| onChoice?.(enableWorktrees); | ||
| close(); | ||
| }} | ||
| /> | ||
| ); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
cloneRepobypassesupsertProject, so cloned projects skip the newly added favicon discovery/init behavior and diverge from other project creation flows.Prompt for AI agents