Fix sidebar project removal

Remove projects through a daemon RPC so the sidebar can delete the project
record after archiving its active workspaces. Reopening a removed directory
recreates the missing project record so local projects keep their derived
basename, and History skips orphaned archived workspace rows.

Fixes #1583.
This commit is contained in:
Mohamed Boudra
2026-06-19 00:46:40 +07:00
parent f876b80f7a
commit 9a09ccb3d6
20 changed files with 736 additions and 29 deletions
+41
View File
@@ -1521,6 +1521,47 @@ test("sends first-agent prompt context with workspace.create.request", async ()
});
});
test("sends project.remove.request", async () => {
const logger = createMockLogger();
const mock = createMockTransport();
const client = new DaemonClient({
url: "ws://test",
clientId: "clsk_unit_test",
logger,
reconnect: { enabled: false },
transportFactory: () => mock.transport,
});
clients.push(client);
const connectPromise = client.connect();
mock.triggerOpen();
await connectPromise;
const removePromise = client.removeProject("remote:github.com/acme/app", "req-remove-project");
expect(parseSentFrame(mock.sent[0])).toEqual({
type: "project.remove.request",
requestId: "req-remove-project",
projectId: "remote:github.com/acme/app",
});
mock.triggerMessage(
wrapSessionMessage({
type: "project.remove.response",
payload: {
requestId: "req-remove-project",
projectId: "remote:github.com/acme/app",
accepted: true,
removedWorkspaceIds: ["ws-main"],
error: null,
},
}),
);
await expect(removePromise).resolves.toEqual({ removedWorkspaceIds: ["ws-main"] });
});
test("sends worktree base-ref fields in create_paseo_worktree_request", async () => {
const logger = createMockLogger();
const mock = createMockTransport();
+18
View File
@@ -2153,6 +2153,24 @@ export class DaemonClient {
return { customName: payload.customName };
}
async removeProject(
projectId: string,
requestId?: string,
): Promise<{ removedWorkspaceIds: string[] }> {
const payload = await this.sendNamespacedCorrelatedSessionRequest<"project.remove.response">({
requestId,
message: {
type: "project.remove.request",
projectId,
},
timeout: 10000,
});
if (!payload.accepted) {
throw new Error(payload.error ?? "removeProject rejected");
}
return { removedWorkspaceIds: payload.removedWorkspaceIds };
}
async setWorkspaceTitle(
workspaceId: string,
title: string | null,