diff --git a/apps/web/components/task/changes-panel.tsx b/apps/web/components/task/changes-panel.tsx
index c31344b422..23f0f11795 100644
--- a/apps/web/components/task/changes-panel.tsx
+++ b/apps/web/components/task/changes-panel.tsx
@@ -43,6 +43,8 @@ type ChangedFile = {
plus: number | undefined;
minus: number | undefined;
oldPath: string | undefined;
+ /** Repository this file belongs to in multi-repo workspaces; empty for single-repo. */
+ repositoryName?: string;
};
function mapToChangedFiles(files: FileInfo[]): ChangedFile[] {
@@ -53,6 +55,7 @@ function mapToChangedFiles(files: FileInfo[]): ChangedFile[] {
plus: file.additions,
minus: file.deletions,
oldPath: file.old_path,
+ repositoryName: file.repository_name,
}));
}
diff --git a/apps/web/hooks/domains/session/use-session-git.ts b/apps/web/hooks/domains/session/use-session-git.ts
index ff65b2d747..688a8ea095 100644
--- a/apps/web/hooks/domains/session/use-session-git.ts
+++ b/apps/web/hooks/domains/session/use-session-git.ts
@@ -1,7 +1,7 @@
"use client";
import { useState, useCallback, useEffect, useMemo } from "react";
-import { useSessionGitStatus } from "./use-session-git-status";
+import { useSessionGitStatus, useSessionGitStatusByRepo } from "./use-session-git-status";
import { useSessionCommits } from "./use-session-commits";
import { useCumulativeDiff } from "./use-cumulative-diff";
import { useGitOperations } from "@/hooks/use-git-operations";
@@ -72,18 +72,44 @@ export type SessionGit = {
) => Promise
;
};
+/**
+ * Builds the SessionGit's flat file list. For multi-repo workspaces it
+ * stamps each FileInfo with its repository_name so consumers can group;
+ * for single-repo it returns the legacy single-status files unchanged.
+ */
+function aggregateFilesAcrossRepos(
+ statusByRepo: ReturnType,
+ gitStatus: ReturnType,
+): FileInfo[] {
+ if (statusByRepo.length > 0) {
+ const out: FileInfo[] = [];
+ for (const { repository_name, status } of statusByRepo) {
+ if (!status?.files) continue;
+ for (const f of Object.values(status.files)) {
+ out.push(repository_name ? { ...f, repository_name } : f);
+ }
+ }
+ return out;
+ }
+ return gitStatus?.files ? Object.values(gitStatus.files) : [];
+}
+
export function useSessionGit(sessionId: string | null | undefined): SessionGit {
const sid = sessionId ?? null;
const gitStatus = useSessionGitStatus(sid);
+ const statusByRepo = useSessionGitStatusByRepo(sid);
const { commits, loading: commitsLoading } = useSessionCommits(sid);
const { diff: cumulativeDiff } = useCumulativeDiff(sid);
const gitOps = useGitOperations(sid);
const [pendingStageFiles, setPendingStageFiles] = useState>(new Set());
+ // Multi-repo: aggregate files from every repo's per-repo status, stamping
+ // each FileInfo with its repository_name so downstream UI can group them.
+ // Single-repo (or repo-less) sessions fall back to the legacy single status.
const allFiles = useMemo(
- () => (gitStatus?.files ? Object.values(gitStatus.files) : []),
- [gitStatus],
+ () => aggregateFilesAcrossRepos(statusByRepo, gitStatus),
+ [statusByRepo, gitStatus],
);
const unstagedFiles = useMemo(() => allFiles.filter((f) => !f.staged), [allFiles]);
const stagedFiles = useMemo(() => allFiles.filter((f) => f.staged), [allFiles]);
diff --git a/apps/web/lib/state/slices/session-runtime/types.ts b/apps/web/lib/state/slices/session-runtime/types.ts
index 02da3f89e9..b24531aeed 100644
--- a/apps/web/lib/state/slices/session-runtime/types.ts
+++ b/apps/web/lib/state/slices/session-runtime/types.ts
@@ -49,6 +49,13 @@ export type FileInfo = {
old_path?: string;
diff?: string;
diff_skip_reason?: "too_large" | "binary" | "truncated" | "budget_exceeded";
+ /**
+ * Repository this file belongs to in multi-repo task workspaces. Stamped
+ * by useSessionGit when aggregating per-repo statuses; empty for single-
+ * repo workspaces. The Changes panel uses it to group files under
+ * per-repository headers.
+ */
+ repository_name?: string;
};
export type GitStatusEntry = {