feat: add onboarding command and migrate git diff to checkout API

This commit is contained in:
Mohamed Boudra
2026-02-12 17:00:35 +07:00
parent 44c32fb8fb
commit 018edef3bb
39 changed files with 2038 additions and 965 deletions
@@ -506,6 +506,7 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
});
const {
status: prStatus,
githubFeaturesEnabled,
payloadError: prPayloadError,
refresh: refreshPrStatus,
} = useCheckoutPrStatusQuery({
@@ -840,7 +841,7 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
const diffErrorMessage =
diffPayloadError?.message ??
(isDiffError && diffError instanceof Error ? diffError.message : null);
const prErrorMessage = prPayloadError?.message ?? null;
const prErrorMessage = githubFeaturesEnabled ? prPayloadError?.message ?? null : null;
const branchLabel =
gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD"
? gitStatus.currentBranch
@@ -993,7 +994,7 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
}
// View PR - when PR exists
if (hasPullRequest && prStatus?.url) {
if (githubFeaturesEnabled && hasPullRequest && prStatus?.url) {
const prUrl = prStatus.url;
allActions.set("view-pr", {
id: "view-pr",
@@ -1008,7 +1009,7 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
}
// Create PR - when ahead of base and no PR
if (aheadCount > 0 && !hasPullRequest) {
if (githubFeaturesEnabled && aheadCount > 0 && !hasPullRequest) {
allActions.set("create-pr", {
id: "create-pr",
label: "Create PR",
@@ -1112,7 +1113,7 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
return { primary, secondary, menu };
}, [
isGit, hasRemote, hasPullRequest, prStatus?.url, aheadCount, isPaseoOwnedWorktree, isOnBaseBranch,
isGit, hasRemote, hasPullRequest, prStatus?.url, aheadCount, isPaseoOwnedWorktree, isOnBaseBranch, githubFeaturesEnabled,
hasUncommittedChanges, aheadOfOrigin, shipDefault, baseRefLabel,
commitDisabled, pushDisabled, prDisabled, mergeDisabled, mergeFromBaseDisabled, archiveDisabled,
commitStatus, pushStatus, prCreateStatus, mergeStatus, mergeFromBaseStatus, archiveStatus,
@@ -322,7 +322,6 @@ export function SessionProvider({
const setPendingPermissions = useSessionStore(
(state) => state.setPendingPermissions
);
const setGitDiffs = useSessionStore((state) => state.setGitDiffs);
const setFileExplorer = useSessionStore((state) => state.setFileExplorer);
const clearDraftInput = useDraftStore((state) => state.clearDraftInput);
const setQueuedMessages = useSessionStore((state) => state.setQueuedMessages);
@@ -666,22 +665,6 @@ export function SessionProvider({
[serverId, setFileExplorer]
);
const gitDiffMutation = useMutation({
mutationFn: async ({ agentId }: { agentId: string }) => {
if (!agentId) {
throw new Error("Agent id is required");
}
if (!client) {
throw new Error("Daemon client unavailable");
}
const payload = await client.getGitDiff(agentId);
if (payload.error) {
throw new Error(payload.error);
}
return { agentId: payload.agentId, diff: payload.diff ?? "" };
},
});
const refreshAgentMutation = useMutation({
mutationFn: async ({ agentId }: { agentId: string }) => {
if (!agentId) {
@@ -1275,15 +1258,6 @@ export function SessionProvider({
return next;
});
setGitDiffs(serverId, (prev) => {
if (!prev.has(agentId)) {
return prev;
}
const next = new Map(prev);
next.delete(agentId);
return next;
});
setFileExplorer(serverId, (prev) => {
if (!prev.has(agentId)) {
return prev;
@@ -1343,7 +1317,6 @@ export function SessionProvider({
setAgents,
setAgentLastActivity,
setPendingPermissions,
setGitDiffs,
setFileExplorer,
setHasHydratedAgents,
updateConnectionStatus,
@@ -1635,24 +1608,6 @@ export function SessionProvider({
[]
);
const requestGitDiff = useCallback(
(agentId: string) => {
gitDiffMutation
.mutateAsync({ agentId })
.then((result) => {
setGitDiffs(serverId, (prev) =>
new Map(prev).set(result.agentId, result.diff)
);
})
.catch((error) => {
setGitDiffs(serverId, (prev) =>
new Map(prev).set(agentId, `Error: ${error.message}`)
);
});
},
[serverId, gitDiffMutation, setGitDiffs]
);
const requestDirectoryListing = useCallback(
(agentId: string, path: string, options?: { recordHistory?: boolean }) => {
const normalizedPath = path && path.length > 0 ? path : ".";
@@ -43,6 +43,7 @@ export function useCheckoutPrStatusQuery({
return {
status: query.data?.status ?? null,
githubFeaturesEnabled: query.data?.githubFeaturesEnabled ?? true,
payloadError: query.data?.error ?? null,
isLoading: query.isLoading,
isFetching: query.isFetching,
@@ -1,70 +0,0 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect } from "react";
import { UnistylesRuntime } from "react-native-unistyles";
import { useSessionStore } from "@/stores/session-store";
import { usePanelStore } from "@/stores/panel-store";
const GIT_DIFF_STALE_TIME = 30_000;
function gitDiffQueryKey(serverId: string, agentId: string) {
return ["gitDiff", serverId, agentId] as const;
}
interface UseGitDiffQueryOptions {
serverId: string;
agentId: string;
}
export function useGitDiffQuery({ serverId, agentId }: UseGitDiffQueryOptions) {
const queryClient = useQueryClient();
const client = useSessionStore(
(state) => state.sessions[serverId]?.client ?? null
);
const isConnected = useSessionStore(
(state) => state.sessions[serverId]?.connection.isConnected ?? false
);
const isMobile =
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
const mobileView = usePanelStore((state) => state.mobileView);
const desktopFileExplorerOpen = usePanelStore((state) => state.desktop.fileExplorerOpen);
const explorerTab = usePanelStore((state) => state.explorerTab);
const isOpen = isMobile ? mobileView === "file-explorer" : desktopFileExplorerOpen;
const query = useQuery({
queryKey: gitDiffQueryKey(serverId, agentId),
queryFn: async () => {
if (!client) {
throw new Error("Daemon client not available");
}
const response = await client.getGitDiff(agentId);
return response.diff;
},
enabled: !!client && isConnected && !!agentId,
staleTime: GIT_DIFF_STALE_TIME,
refetchInterval: 10_000,
});
// Revalidate when sidebar opens with "changes" tab active
useEffect(() => {
if (!isOpen || explorerTab !== "changes" || !agentId) {
return;
}
// Invalidate to trigger background refetch (shows stale data while fetching)
queryClient.invalidateQueries({
queryKey: gitDiffQueryKey(serverId, agentId),
});
}, [isOpen, explorerTab, serverId, agentId, queryClient]);
const refresh = useCallback(() => {
return query.refetch();
}, [query]);
return {
diff: query.data ?? null,
isLoading: query.isLoading,
isFetching: query.isFetching,
isError: query.isError,
error: query.error,
refresh,
};
}
-29
View File
@@ -201,9 +201,6 @@ export interface SessionState {
// Permissions
pendingPermissions: Map<string, PendingPermission>;
// Git diffs
gitDiffs: Map<string, string>;
// File explorer
fileExplorer: Map<string, AgentFileExplorerState>;
@@ -258,9 +255,6 @@ interface SessionStoreActions {
// Permissions
setPendingPermissions: (serverId: string, perms: Map<string, PendingPermission> | ((prev: Map<string, PendingPermission>) => Map<string, PendingPermission>)) => void;
// Git diffs
setGitDiffs: (serverId: string, diffs: Map<string, string> | ((prev: Map<string, string>) => Map<string, string>)) => void;
// File explorer
setFileExplorer: (serverId: string, state: Map<string, AgentFileExplorerState> | ((prev: Map<string, AgentFileExplorerState>) => Map<string, AgentFileExplorerState>)) => void;
@@ -332,7 +326,6 @@ function createInitialSessionState(serverId: string, client: DaemonClient, audio
initializingAgents: new Map(),
agents: new Map(),
pendingPermissions: new Map(),
gitDiffs: new Map(),
fileExplorer: new Map(),
queuedMessages: new Map(),
};
@@ -745,28 +738,6 @@ export const useSessionStore = create<SessionStore>()(
});
},
// Git diffs
setGitDiffs: (serverId, diffs) => {
set((prev) => {
const session = prev.sessions[serverId];
if (!session) {
return prev;
}
const nextDiffs = typeof diffs === "function" ? diffs(session.gitDiffs) : diffs;
if (session.gitDiffs === nextDiffs) {
return prev;
}
logSessionStoreUpdate("setGitDiffs", serverId, { count: nextDiffs.size });
return {
...prev,
sessions: {
...prev.sessions,
[serverId]: { ...session, gitDiffs: nextDiffs },
},
};
});
},
// File explorer
setFileExplorer: (serverId, state) => {
set((prev) => {