-
Notifications
You must be signed in to change notification settings - Fork 966
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
base: main
Are you sure you want to change the base?
Changes from all 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, | ||
|
|
@@ -116,6 +117,27 @@ function upsertProject(mainRepoPath: string, defaultBranch: string): Project { | |
| .set({ lastOpenedAt: Date.now(), defaultBranch }) | ||
| .where(eq(projects.id, existing.id)) | ||
| .run(); | ||
|
|
||
| // Discover favicon if no icon is set yet (fire-and-forget) | ||
| if (!existing.iconUrl) { | ||
|
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: Auto-discovery treats Prompt for AI agents |
||
| discoverAndSaveProjectIcon({ | ||
| projectId: existing.id, | ||
| repoPath: mainRepoPath, | ||
| }) | ||
| .then((iconUrl) => { | ||
| if (iconUrl) { | ||
| localDb | ||
| .update(projects) | ||
| .set({ iconUrl }) | ||
| .where(eq(projects.id, existing.id)) | ||
| .run(); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| console.error("[upsertProject] Favicon discovery failed:", err); | ||
| }); | ||
| } | ||
|
|
||
| return { ...existing, lastOpenedAt: Date.now(), defaultBranch }; | ||
| } | ||
|
|
||
|
|
@@ -130,6 +152,24 @@ function upsertProject(mainRepoPath: string, defaultBranch: string): Project { | |
| .returning() | ||
| .get(); | ||
|
|
||
| // Discover favicon for new project (fire-and-forget) | ||
| discoverAndSaveProjectIcon({ | ||
| projectId: project.id, | ||
| repoPath: mainRepoPath, | ||
| }) | ||
| .then((iconUrl) => { | ||
| if (iconUrl) { | ||
| localDb | ||
| .update(projects) | ||
| .set({ iconUrl }) | ||
| .where(eq(projects.id, project.id)) | ||
| .run(); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| console.error("[upsertProject] Favicon discovery failed:", err); | ||
| }); | ||
|
|
||
| return project; | ||
| } | ||
|
|
||
|
|
@@ -1230,6 +1270,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 +1309,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 +1410,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 +1443,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 |
|---|---|---|
|
|
@@ -6,8 +6,9 @@ import { | |
| saveProjectIconFromFile, | ||
| } from "main/lib/project-icons"; | ||
|
|
||
| /** Common favicon file names to search for in project roots */ | ||
| /** Common favicon file names to search for in project roots and subdirectories */ | ||
| const FAVICON_PATTERNS = [ | ||
| // Root level | ||
| "favicon.ico", | ||
| "favicon.png", | ||
| "favicon.svg", | ||
|
|
@@ -17,6 +18,7 @@ const FAVICON_PATTERNS = [ | |
| "icon.svg", | ||
| ".github/logo.png", | ||
| ".github/logo.svg", | ||
| // Common static/public directories | ||
| "public/favicon.ico", | ||
| "public/favicon.png", | ||
| "public/favicon.svg", | ||
|
|
@@ -28,6 +30,19 @@ const FAVICON_PATTERNS = [ | |
| "assets/favicon.ico", | ||
| "assets/favicon.png", | ||
| "assets/icon.png", | ||
| // Next.js / app directory patterns (up to 2 levels deep) | ||
| "app/favicon.ico", | ||
| "app/icon.png", | ||
| "app/icon.svg", | ||
| "**/app/favicon.ico", | ||
| "**/app/icon.png", | ||
| "**/app/icon.svg", | ||
| // Deeper public directories in monorepos/nested projects | ||
| "**/public/favicon.ico", | ||
|
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: Selecting Prompt for AI agents |
||
| "**/public/favicon.png", | ||
| "**/public/favicon.svg", | ||
| "**/public/logo.png", | ||
| "**/public/logo.svg", | ||
| ]; | ||
|
|
||
| /** Max file size for discovered favicons: 256KB */ | ||
|
|
@@ -48,6 +63,7 @@ export async function discoverAndSaveProjectIcon({ | |
| const matches = await fg(FAVICON_PATTERNS, { | ||
| cwd: repoPath, | ||
| absolute: true, | ||
| deep: 4, | ||
| ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"], | ||
| onlyFiles: true, | ||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { projects, workspaces, worktrees } from "@superset/local-db"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { projects, settings, workspaces, worktrees } from "@superset/local-db"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { and, eq, isNull, not } from "drizzle-orm"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { track } from "main/lib/analytics"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { localDb } from "main/lib/local-db"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -294,6 +294,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 }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -306,6 +307,92 @@ 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"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Checkout the target branch so the main repo matches the workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input.branchName?.trim()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await safeCheckoutBranch(project.mainRepoPath, 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(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 |
|---|---|---|
|
|
@@ -153,6 +153,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[]; | ||
|
|
@@ -185,6 +186,7 @@ export const createQueryProcedures = () => { | |
| mainRepoPath: project.mainRepoPath, | ||
| hideImage: project.hideImage ?? false, | ||
| iconUrl: project.iconUrl ?? null, | ||
| worktreeMode: project.worktreeMode ?? null, | ||
| }, | ||
| workspaces: [], | ||
| sections: projectSections, | ||
|
|
||
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