Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions docs/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,17 @@ emptied duplicate.

Array of workspace records. A workspace is a specific working directory within a project.

| Field | Type | Description |
| ------------- | ----------------------------------------------- | ------------------------------ |
| `workspaceId` | `string` | Primary key |
| `projectId` | `string` | FK to Project.projectId |
| `cwd` | `string` | Filesystem path |
| `kind` | `"local_checkout" \| "worktree" \| "directory"` | |
| `displayName` | `string` | |
| `createdAt` | `string` (ISO 8601) | |
| `updatedAt` | `string` (ISO 8601) | |
| `archivedAt` | `string \| null` (ISO 8601) | Soft-delete; required nullable |
| Field | Type | Description |
| --------------------- | ----------------------------------------------- | ---------------------------------------------------------------------- |
| `workspaceId` | `string` | Primary key |
| `projectId` | `string` | FK to Project.projectId |
| `cwd` | `string` | Filesystem path |
| `kind` | `"local_checkout" \| "worktree" \| "directory"` | |
| `displayName` | `string` | |
| `worktreeStoragePath` | `string \| null` | Optional local root for new Paseo-created worktrees for this workspace |
| `createdAt` | `string` (ISO 8601) | |
| `updatedAt` | `string` (ISO 8601) | |
| `archivedAt` | `string \| null` (ISO 8601) | Soft-delete; required nullable |

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function workspace(input: {
name: input.name,
status: input.status ?? "done",
archivingAt: null,
worktreeStoragePath: null,
diffStat: null,
scripts: input.scripts ?? [],
};
Expand Down
17 changes: 15 additions & 2 deletions packages/app/src/components/workspace-setup-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ async function callWorkspaceCreation({
}: {
creationMethod: "create_worktree" | "open_project";
connectedClient: DaemonClient;
input: { cwd: string };
input: { cwd: string; worktreeStoragePath?: string | null };
}) {
if (creationMethod === "create_worktree") {
return connectedClient.createPaseoWorktree({
cwd: input.cwd,
...(input.worktreeStoragePath ? { worktreeStoragePath: input.worktreeStoragePath } : {}),
worktreeSlug: createNameId(),
});
}
Expand Down Expand Up @@ -158,6 +159,17 @@ export function WorkspaceSetupDialog() {
const workspace = createdWorkspace;
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const worktreeStoragePath = useSessionStore((state) => {
if (!pendingWorkspaceSetup) {
return null;
}
const sourceWorkspace = pendingWorkspaceSetup.sourceWorkspaceId
? state.sessions[pendingWorkspaceSetup.serverId]?.workspaces.get(
pendingWorkspaceSetup.sourceWorkspaceId,
)
: null;
return sourceWorkspace?.worktreeStoragePath ?? null;
});
const chatDraft = useAgentInputDraft({
draftKey: `workspace-setup:${serverId}:${sourceDirectory}`,
composer: buildChatDraftComposerArgs({
Expand Down Expand Up @@ -237,7 +249,7 @@ export function WorkspaceSetupDialog() {
const payload = await callWorkspaceCreation({
creationMethod: pendingWorkspaceSetup.creationMethod,
connectedClient,
input,
input: { ...input, worktreeStoragePath },
});

if (payload.error || !payload.workspace) {
Expand All @@ -260,6 +272,7 @@ export function WorkspaceSetupDialog() {
pendingWorkspaceSetup,
setHasHydratedWorkspaces,
withConnectedClient,
worktreeStoragePath,
],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function workspaceDescriptor(input: {
name: input.name ?? input.id,
status: "done",
archivingAt: null,
worktreeStoragePath: null,
diffStat: null,
scripts: [],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function workspace(input: {
name: "main",
status: "running",
archivingAt: null,
worktreeStoragePath: null,
diffStat: null,
scripts: input.scripts ?? [],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const baseWorkspace: WorkspaceDescriptor = {
name: "feature",
status: "done",
archivingAt: "2026-04-30T00:00:00.000Z",
worktreeStoragePath: null,
diffStat: null,
scripts: [],
};
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/git/actions-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ function workspace(input: Partial<WorkspaceDescriptor> & Pick<WorkspaceDescripto
name: input.name ?? input.id,
status: input.status ?? "done",
archivingAt: input.archivingAt ?? null,
worktreeStoragePath: input.worktreeStoragePath ?? null,
diffStat: input.diffStat ?? null,
scripts: input.scripts ?? [],
} satisfies WorkspaceDescriptor;
Expand Down
16 changes: 10 additions & 6 deletions packages/app/src/hooks/use-active-worktree-new-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ export function useActiveWorktreeNewAction() {
const serverId = selection?.serverId ?? null;
const workspaceId = selection?.workspaceId ?? null;

const workingDir = useSessionStore((state) => {
const routeContext = useSessionStore((state) => {
if (!serverId || !workspaceId) {
return null;
}
const workspace = state.sessions[serverId]?.workspaces?.get(workspaceId);
if (!workspace || workspace.projectKind !== "git") {
return null;
}
return workspace.projectRootPath;
return {
workingDir: workspace.projectRootPath,
projectId: workspace.projectId,
};
});

const displayName = useSessionStore((state) => {
Expand All @@ -37,21 +40,22 @@ export function useActiveWorktreeNewAction() {
});

const handle = useCallback(() => {
if (!serverId || !workingDir) {
if (!serverId || !routeContext) {
return false;
}
router.navigate(
buildHostNewWorkspaceRoute(serverId, workingDir, {
buildHostNewWorkspaceRoute(serverId, routeContext.workingDir, {
displayName: displayName ?? undefined,
projectId: routeContext.projectId,
}) as never,
);
return true;
}, [serverId, workingDir, displayName]);
}, [serverId, routeContext, displayName]);

useKeyboardActionHandler({
handlerId: "worktree-new-active",
actions: WORKTREE_NEW_ACTIONS,
enabled: serverId !== null && workingDir !== null,
enabled: serverId !== null && routeContext !== null,
priority: 0,
handle,
});
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/hooks/use-open-project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function buildWorkspacePayload() {
workspaceKind: "checkout" as const,
name: "project",
archivingAt: null,
worktreeStoragePath: null,
status: "done" as const,
activityAt: null,
diffStat: null,
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/hooks/use-projects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function workspace(input: {
workspaceKind: "local_checkout",
name: input.id,
archivingAt: null,
worktreeStoragePath: null,
status: "done",
activityAt: null,
diffStat: null,
Expand Down
18 changes: 17 additions & 1 deletion packages/app/src/screens/new-workspace-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,21 @@ export function NewWorkspaceScreen({
const isPending = pendingAction !== null;
const client = useHostRuntimeClient(serverId);
const isConnected = useHostRuntimeIsConnected(serverId);
const worktreeStoragePath = useSessionStore((state) => {
const session = state.sessions[serverId];
if (!session) {
return null;
}
const workspaces = Array.from(session.workspaces.values());
const sourceWorkspace =
workspaces.find(
(entry) => entry.projectId === projectId && entry.projectRootPath === sourceDirectory,
) ??
workspaces.find((entry) => entry.projectRootPath === sourceDirectory) ??
workspaces.find((entry) => entry.workspaceDirectory === sourceDirectory) ??
null;
return sourceWorkspace?.worktreeStoragePath ?? null;
});
const draftKey = `new-workspace:${serverId}:${sourceDirectory}`;
const chatDraft = useAgentInputDraft({
draftKey,
Expand Down Expand Up @@ -718,6 +733,7 @@ export function NewWorkspaceScreen({
return {
cwd: input.cwd,
...(projectId ? { projectId } : {}),
...(worktreeStoragePath ? { worktreeStoragePath } : {}),
worktreeSlug: createNameId(),
...(hasFirstAgentContext
? {
Expand All @@ -730,7 +746,7 @@ export function NewWorkspaceScreen({
...checkoutRequest,
};
},
[currentBranch, projectId, selectedItem],
[currentBranch, projectId, selectedItem, worktreeStoragePath],
);

const ensureWorkspace = useCallback(
Expand Down
Loading