From 953902dcced6b4d8169fce8ca46fc59f14088ce5 Mon Sep 17 00:00:00 2001 From: Hiep Le <69354317+hieptl@users.noreply.github.com> Date: Thu, 31 Jul 2025 21:42:07 +0700 Subject: [PATCH] feat(frontend): integrate with the updated get microagents API for the microagent management page. (#10010) --- .../microagent-management.test.tsx | 165 +++++++++--------- frontend/src/api/open-hands.ts | 24 ++- frontend/src/api/open-hands.types.ts | 7 + .../microagent-management-microagent-card.tsx | 22 +-- ...ent-management-upsert-microagent-modal.tsx | 26 ++- ...ent-management-view-microagent-content.tsx | 82 ++++----- .../use-repository-microagent-content.ts | 17 ++ frontend/src/i18n/declaration.ts | 1 + frontend/src/i18n/translation.json | 16 ++ frontend/src/types/microagent-management.tsx | 5 - frontend/src/utils/utils.ts | 19 ++ 11 files changed, 223 insertions(+), 161 deletions(-) create mode 100644 frontend/src/hooks/query/use-repository-microagent-content.ts diff --git a/frontend/__tests__/components/features/microagent-management/microagent-management.test.tsx b/frontend/__tests__/components/features/microagent-management/microagent-management.test.tsx index f9ac19043d..e9e312ccf9 100644 --- a/frontend/__tests__/components/features/microagent-management/microagent-management.test.tsx +++ b/frontend/__tests__/components/features/microagent-management/microagent-management.test.tsx @@ -105,22 +105,12 @@ describe("MicroagentManagement", () => { const mockMicroagents: RepositoryMicroagent[] = [ { name: "test-microagent-1", - type: "repo", - content: "Test microagent content 1", - triggers: ["test", "microagent"], - inputs: [], - tools: [], created_at: "2021-10-01T12:00:00Z", git_provider: "github", path: ".openhands/microagents/test-microagent-1", }, { name: "test-microagent-2", - type: "knowledge", - content: "Test microagent content 2", - triggers: ["knowledge", "test"], - inputs: [], - tools: [], created_at: "2021-10-02T12:00:00Z", git_provider: "github", path: ".openhands/microagents/test-microagent-2", @@ -173,6 +163,13 @@ describe("MicroagentManagement", () => { vi.spyOn(OpenHands, "searchConversations").mockResolvedValue([ ...mockConversations, ]); + // Setup default mock for getRepositoryMicroagentContent + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "Original microagent content for testing updates", + path: ".openhands/microagents/update-test-microagent", + git_provider: "github", + triggers: ["test", "update"], + }); }); it("should render the microagent management page", async () => { @@ -1187,17 +1184,6 @@ describe("MicroagentManagement", () => { expect(conversation1).toBeInTheDocument(); expect(conversation2).toBeInTheDocument(); - - // Check that created dates are displayed for conversations (there are multiple elements with the same text) - const createdDates = screen.getAllByText( - /COMMON\$CREATED_ON.*10\/01\/2021/, - ); - expect(createdDates.length).toBeGreaterThan(0); - - const createdDates2 = screen.getAllByText( - /COMMON\$CREATED_ON.*10\/02\/2021/, - ); - expect(createdDates2.length).toBeGreaterThan(0); }); it("should handle multiple repository expansions with conversations", async () => { @@ -1475,11 +1461,6 @@ describe("MicroagentManagement", () => { describe("MicroagentManagementMain", () => { const mockRepositoryMicroagent: RepositoryMicroagent = { name: "test-microagent", - type: "repo", - content: "Test microagent content", - triggers: ["test", "microagent"], - inputs: [], - tools: [], created_at: "2021-10-01T12:00:00Z", git_provider: "github", path: ".openhands/microagents/test-microagent", @@ -1820,11 +1801,6 @@ describe("MicroagentManagement", () => { it("should handle microagent with all required properties", async () => { const completeMicroagent: RepositoryMicroagent = { name: "complete-microagent", - type: "knowledge", - content: "Complete microagent content with all properties", - triggers: ["complete", "test"], - inputs: ["input1", "input2"], - tools: ["tool1", "tool2"], created_at: "2021-10-01T12:00:00Z", git_provider: "github", path: ".openhands/microagents/complete-microagent", @@ -1874,11 +1850,6 @@ describe("MicroagentManagement", () => { describe("Update microagent functionality", () => { const mockMicroagentForUpdate: RepositoryMicroagent = { name: "update-test-microagent", - type: "repo", - content: "Original microagent content for testing updates", - triggers: ["original", "test"], - inputs: [], - tools: [], created_at: "2021-10-01T12:00:00Z", git_provider: "github", path: ".openhands/microagents/update-test-microagent", @@ -1999,11 +1970,13 @@ describe("MicroagentManagement", () => { }, }); - // Check that the form fields are populated with existing data - const queryInput = screen.getByTestId("query-input"); - expect(queryInput).toHaveValue( - "Original microagent content for testing updates", - ); + // Wait for the content to be loaded and form fields to be populated + await waitFor(() => { + const queryInput = screen.getByTestId("query-input"); + expect(queryInput).toHaveValue( + "Original microagent content for testing updates", + ); + }); }); it("should handle update microagent form submission", async () => { @@ -2207,12 +2180,16 @@ describe("MicroagentManagement", () => { it("should handle update modal with microagent that has no content", async () => { const user = userEvent.setup(); - const microagentWithoutContent = { - ...mockMicroagentForUpdate, - content: "", - }; - // Render with update modal visible and microagent without content + // Mock the content API to return empty content for this test + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "", + path: ".openhands/microagents/update-test-microagent", + git_provider: "github", + triggers: [], + }); + + // Render with update modal visible and microagent renderWithProviders(, { preloadedState: { metrics: { @@ -2222,7 +2199,7 @@ describe("MicroagentManagement", () => { }, microagentManagement: { selectedMicroagentItem: { - microagent: microagentWithoutContent, + microagent: mockMicroagentForUpdate, conversation: undefined, }, addMicroagentModalVisible: false, @@ -2243,19 +2220,25 @@ describe("MicroagentManagement", () => { }, }); - // Check that the form field is empty - const queryInput = screen.getByTestId("query-input"); - expect(queryInput).toHaveValue(""); + // Wait for the content to be loaded and check that the form field is empty + await waitFor(() => { + const queryInput = screen.getByTestId("query-input"); + expect(queryInput).toHaveValue(""); + }); }); it("should handle update modal with microagent that has no triggers", async () => { const user = userEvent.setup(); - const microagentWithoutTriggers = { - ...mockMicroagentForUpdate, - triggers: [], - }; - // Render with update modal visible and microagent without triggers + // Mock the content API to return content without triggers for this test + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "Original microagent content for testing updates", + path: ".openhands/microagents/update-test-microagent", + git_provider: "github", + triggers: [], + }); + + // Render with update modal visible and microagent renderWithProviders(, { preloadedState: { metrics: { @@ -2265,7 +2248,7 @@ describe("MicroagentManagement", () => { }, microagentManagement: { selectedMicroagentItem: { - microagent: microagentWithoutTriggers, + microagent: mockMicroagentForUpdate, conversation: undefined, }, addMicroagentModalVisible: false, @@ -2397,11 +2380,6 @@ describe("MicroagentManagement", () => { getRepositoryMicroagentsSpy.mockResolvedValue([ { name: "test-microagent", - type: "repo", - content: "Test content", - triggers: [], - inputs: [], - tools: [], created_at: "2021-10-01", git_provider: "github", path: ".openhands/microagents/test", @@ -2486,11 +2464,6 @@ describe("MicroagentManagement", () => { describe("Learn something new button functionality", () => { const mockMicroagentForLearn: RepositoryMicroagent = { name: "learn-test-microagent", - type: "repo", - content: "Test microagent content for learn functionality", - triggers: ["learn", "test"], - inputs: [], - tools: [], created_at: "2021-10-01T12:00:00Z", git_provider: "github", path: ".openhands/microagents/learn-test-microagent", @@ -2586,6 +2559,14 @@ describe("MicroagentManagement", () => { it("should populate form fields with current microagent data when learn button is clicked", async () => { const user = userEvent.setup(); + // Mock the content API to return the expected content for this test + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "Test microagent content for learn functionality", + path: ".openhands/microagents/learn-test-microagent", + git_provider: "github", + triggers: ["learn", "test"], + }); + // Render with selected microagent renderWithProviders(, { preloadedState: { @@ -2626,21 +2607,27 @@ describe("MicroagentManagement", () => { expect(screen.getByTestId("add-microagent-modal")).toBeInTheDocument(); }); - // Check that the form fields are populated with current microagent data - const queryInput = screen.getByTestId("query-input"); - expect(queryInput).toHaveValue( - "Test microagent content for learn functionality", - ); + // Wait for the content to be loaded and form to be populated + await waitFor(() => { + const queryInput = screen.getByTestId("query-input"); + expect(queryInput).toHaveValue( + "Test microagent content for learn functionality", + ); + }); }); it("should handle learn button click with microagent that has no content", async () => { const user = userEvent.setup(); - const microagentWithoutContent = { - ...mockMicroagentForLearn, - content: "", - }; - // Render with selected microagent without content + // Mock the content API to return empty content for this test + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "", + path: ".openhands/microagents/learn-test-microagent", + git_provider: "github", + triggers: [], + }); + + // Render with selected microagent renderWithProviders(, { preloadedState: { metrics: { @@ -2650,7 +2637,7 @@ describe("MicroagentManagement", () => { }, microagentManagement: { selectedMicroagentItem: { - microagent: microagentWithoutContent, + microagent: mockMicroagentForLearn, conversation: undefined, }, addMicroagentModalVisible: false, @@ -2680,19 +2667,25 @@ describe("MicroagentManagement", () => { expect(screen.getByTestId("add-microagent-modal")).toBeInTheDocument(); }); - // Check that the form field is empty - const queryInput = screen.getByTestId("query-input"); - expect(queryInput).toHaveValue(""); + // Wait for the content to be loaded and check that the form field is empty + await waitFor(() => { + const queryInput = screen.getByTestId("query-input"); + expect(queryInput).toHaveValue(""); + }); }); it("should handle learn button click with microagent that has no triggers", async () => { const user = userEvent.setup(); - const microagentWithoutTriggers = { - ...mockMicroagentForLearn, - triggers: [], - }; - // Render with selected microagent without triggers + // Mock the content API to return content without triggers for this test + vi.spyOn(OpenHands, "getRepositoryMicroagentContent").mockResolvedValue({ + content: "Test microagent content for learn functionality", + path: ".openhands/microagents/learn-test-microagent", + git_provider: "github", + triggers: [], + }); + + // Render with selected microagent renderWithProviders(, { preloadedState: { metrics: { @@ -2702,7 +2695,7 @@ describe("MicroagentManagement", () => { }, microagentManagement: { selectedMicroagentItem: { - microagent: microagentWithoutTriggers, + microagent: mockMicroagentForLearn, conversation: undefined, }, addMicroagentModalVisible: false, diff --git a/frontend/src/api/open-hands.ts b/frontend/src/api/open-hands.ts index cf0127968a..d87724a7a2 100644 --- a/frontend/src/api/open-hands.ts +++ b/frontend/src/api/open-hands.ts @@ -14,6 +14,7 @@ import { GetMicroagentsResponse, GetMicroagentPromptResponse, CreateMicroagent, + MicroagentContentResponse, } from "./open-hands.types"; import { openHands } from "./open-hands-axios"; import { ApiSettings, PostApiSettings, Provider } from "#/types/settings"; @@ -524,7 +525,7 @@ class OpenHands { } /** - * Get the available microagents for a specific repository + * Get the available microagents for a repository * @param owner The repository owner * @param repo The repository name * @returns The available microagents for the repository @@ -539,6 +540,27 @@ class OpenHands { return data; } + /** + * Get the content of a specific microagent from a repository + * @param owner The repository owner + * @param repo The repository name + * @param filePath The path to the microagent file within the repository + * @returns The microagent content and metadata + */ + static async getRepositoryMicroagentContent( + owner: string, + repo: string, + filePath: string, + ): Promise { + const { data } = await openHands.get( + `/api/user/repository/${owner}/${repo}/microagents/content`, + { + params: { file_path: filePath }, + }, + ); + return data; + } + static async getMicroagentPrompt( conversationId: string, eventId: number, diff --git a/frontend/src/api/open-hands.types.ts b/frontend/src/api/open-hands.types.ts index f03e00931b..394335dfc8 100644 --- a/frontend/src/api/open-hands.types.ts +++ b/frontend/src/api/open-hands.types.ts @@ -147,3 +147,10 @@ export interface CreateMicroagent { git_provider?: Provider; title?: string; } + +export interface MicroagentContentResponse { + content: string; + path: string; + git_provider: Provider; + triggers: string[]; +} diff --git a/frontend/src/components/features/microagent-management/microagent-management-microagent-card.tsx b/frontend/src/components/features/microagent-management/microagent-management-microagent-card.tsx index a4e50c97ab..2569721b94 100644 --- a/frontend/src/components/features/microagent-management/microagent-management-microagent-card.tsx +++ b/frontend/src/components/features/microagent-management/microagent-management-microagent-card.tsx @@ -2,7 +2,6 @@ import { useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; -import { formatDateMMDDYYYY } from "#/utils/format-time-delta"; import { RepositoryMicroagent } from "#/types/microagent-management"; import { Conversation } from "#/api/open-hands.types"; import { @@ -38,22 +37,6 @@ export function MicroagentManagementMicroagentCard({ pr_number: prNumber, } = conversation ?? {}; - // Format the repository URL to point to the microagent file - const microagentFilePath = microagent - ? `.openhands/microagents/${microagent.name}` - : ""; - - // Format the createdAt date using MM/DD/YYYY format - const formattedCreatedAt = useMemo(() => { - if (microagent) { - return formatDateMMDDYYYY(new Date(microagent.created_at)); - } - if (conversation) { - return formatDateMMDDYYYY(new Date(conversation.created_at)); - } - return ""; - }, [microagent, conversation]); - const hasPr = !!(prNumber && prNumber.length > 0); // Helper function to get status text @@ -131,12 +114,9 @@ export function MicroagentManagementMicroagentCard({
{cardTitle}
{!!microagent && (
- {microagentFilePath} + {microagent.path}
)} -
- {t(I18nKey.COMMON$CREATED_ON)} {formattedCreatedAt} -
); diff --git a/frontend/src/components/features/microagent-management/microagent-management-upsert-microagent-modal.tsx b/frontend/src/components/features/microagent-management/microagent-management-upsert-microagent-modal.tsx index 0c8f414d45..0f9f9494cc 100644 --- a/frontend/src/components/features/microagent-management/microagent-management-upsert-microagent-modal.tsx +++ b/frontend/src/components/features/microagent-management/microagent-management-upsert-microagent-modal.tsx @@ -8,11 +8,12 @@ import { BrandButton } from "../settings/brand-button"; import { I18nKey } from "#/i18n/declaration"; import { RootState } from "#/store"; import XIcon from "#/icons/x.svg?react"; -import { cn } from "#/utils/utils"; +import { cn, extractRepositoryInfo } from "#/utils/utils"; import { BadgeInput } from "#/components/shared/inputs/badge-input"; import { MicroagentFormData } from "#/types/microagent-management"; import { Branch, GitRepository } from "#/types/git"; import { useRepositoryBranches } from "#/hooks/query/use-repository-branches"; +import { useRepositoryMicroagentContent } from "#/hooks/query/use-repository-microagent-content"; import { BranchDropdown, BranchLoadingState, @@ -51,13 +52,23 @@ export function MicroagentManagementUpsertMicroagentModal({ // Add a ref to track if the branch was manually cleared by the user const branchManuallyClearedRef = useRef(false); + // Extract owner and repo from full_name for content API + const { owner, repo, filePath } = extractRepositoryInfo( + selectedRepository, + microagent, + ); + + // Fetch microagent content when updating + const { data: microagentContentData, isLoading: isLoadingContent } = + useRepositoryMicroagentContent(owner, repo, filePath, true); + // Populate form fields with existing microagent data when updating useEffect(() => { - if (isUpdate && microagent) { - setQuery(microagent.content); - setTriggers(microagent.triggers || []); + if (isUpdate && microagentContentData) { + setQuery(microagentContentData.content); + setTriggers(microagentContentData.triggers || []); } - }, [isUpdate, microagent]); + }, [isUpdate, microagentContentData]); const { data: branches, @@ -294,10 +305,11 @@ export function MicroagentManagementUpsertMicroagentModal({ isLoading || isLoadingBranches || !selectedBranch || - isBranchesError + isBranchesError || + (isUpdate && isLoadingContent) // Disable while loading content for updates } > - {isLoading || isLoadingBranches + {isLoading || isLoadingBranches || (isUpdate && isLoadingContent) ? t(I18nKey.HOME$LOADING) : t(I18nKey.MICROAGENT$LAUNCH)} diff --git a/frontend/src/components/features/microagent-management/microagent-management-view-microagent-content.tsx b/frontend/src/components/features/microagent-management/microagent-management-view-microagent-content.tsx index de0437c454..fa2776e452 100644 --- a/frontend/src/components/features/microagent-management/microagent-management-view-microagent-content.tsx +++ b/frontend/src/components/features/microagent-management/microagent-management-view-microagent-content.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from "react-i18next"; +import { Spinner } from "@heroui/react"; import { useSelector } from "react-redux"; import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; @@ -7,8 +9,12 @@ import { ul, ol } from "../markdown/list"; import { paragraph } from "../markdown/paragraph"; import { anchor } from "../markdown/anchor"; import { RootState } from "#/store"; +import { useRepositoryMicroagentContent } from "#/hooks/query/use-repository-microagent-content"; +import { I18nKey } from "#/i18n/declaration"; +import { extractRepositoryInfo } from "#/utils/utils"; export function MicroagentManagementViewMicroagentContent() { + const { t } = useTranslation(); const { selectedMicroagentItem } = useSelector( (state: RootState) => state.microagentManagement, ); @@ -19,55 +25,49 @@ export function MicroagentManagementViewMicroagentContent() { const { microagent } = selectedMicroagentItem ?? {}; - const transformMicroagentContent = (): string => { - if (!microagent) { - return ""; - } + // Extract owner and repo from full_name (e.g., "owner/repo") + const { owner, repo, filePath } = extractRepositoryInfo( + selectedRepository, + microagent, + ); - // If no triggers exist, return the content as-is - if (!microagent.triggers || microagent.triggers.length === 0) { - return microagent.content; - } - - // Create the triggers frontmatter - const triggersFrontmatter = ` - --- - - triggers: - ${microagent.triggers.map((trigger) => ` - ${trigger}`).join("\n")} - - --- - `; - - // Prepend the frontmatter to the content - return ` - ${triggersFrontmatter} - - ${microagent.content} - `; - }; + // Fetch microagent content using the new API + const { + data: microagentData, + isLoading, + error, + } = useRepositoryMicroagentContent(owner, repo, filePath, true); if (!microagent || !selectedRepository) { return null; } - // Transform the content to include triggers frontmatter if applicable - const transformedContent = transformMicroagentContent(); - return (
- - {transformedContent} - + {isLoading && ( +
+ +
+ )} + {error && ( +
+ {t(I18nKey.MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT)} +
+ )} + {microagentData && !isLoading && !error && ( + + {microagentData.content} + + )}
); } diff --git a/frontend/src/hooks/query/use-repository-microagent-content.ts b/frontend/src/hooks/query/use-repository-microagent-content.ts new file mode 100644 index 0000000000..4b1979ce62 --- /dev/null +++ b/frontend/src/hooks/query/use-repository-microagent-content.ts @@ -0,0 +1,17 @@ +import { useQuery } from "@tanstack/react-query"; +import OpenHands from "#/api/open-hands"; + +export const useRepositoryMicroagentContent = ( + owner: string, + repo: string, + filePath: string, + cacheDisabled: boolean = false, +) => + useQuery({ + queryKey: ["repository", "microagent", "content", owner, repo, filePath], + queryFn: () => + OpenHands.getRepositoryMicroagentContent(owner, repo, filePath), + enabled: !!owner && !!repo && !!filePath, + staleTime: cacheDisabled ? 0 : 1000 * 60 * 5, // 5 minutes + gcTime: cacheDisabled ? 0 : 1000 * 60 * 15, // 15 minutes + }); diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts index 8894dec247..b7c100d8eb 100644 --- a/frontend/src/i18n/declaration.ts +++ b/frontend/src/i18n/declaration.ts @@ -734,4 +734,5 @@ export enum I18nKey { MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$WHAT_YOU_WOULD_LIKE_TO_KNOW_ABOUT_THIS_REPO", MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO = "MICROAGENT_MANAGEMENT$DESCRIBE_WHAT_TO_KNOW_ABOUT_THIS_REPO", MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION = "MICROAGENT_MANAGEMENT$UPDATE_MICROAGENT_MODAL_DESCRIPTION", + MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT = "MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT", } diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json index cb6e6e3c5f..7129030498 100644 --- a/frontend/src/i18n/translation.json +++ b/frontend/src/i18n/translation.json @@ -11742,5 +11742,21 @@ "tr": "OpenHands, talimatlarınıza göre mikro ajanı güncelleyecektir.", "de": "OpenHands aktualisiert den Microagenten basierend auf Ihren Anweisungen.", "uk": "OpenHands оновить мікроагента відповідно до ваших інструкцій." + }, + "MICROAGENT_MANAGEMENT$ERROR_LOADING_MICROAGENT_CONTENT": { + "en": "Error loading microagent content.", + "ja": "マイクロエージェントのコンテンツの読み込み中にエラーが発生しました。", + "zh-CN": "加载微代理内容时出错。", + "zh-TW": "載入微代理內容時發生錯誤。", + "ko-KR": "마이크로에이전트 콘텐츠를 불러오는 중 오류가 발생했습니다.", + "no": "Feil ved lasting av mikroagent-innhold.", + "it": "Errore durante il caricamento del contenuto del microagente.", + "pt": "Erro ao carregar o conteúdo do microagente.", + "es": "Error al cargar el contenido del microagente.", + "ar": "حدث خطأ أثناء تحميل محتوى الوكيل الدقيق.", + "fr": "Erreur lors du chargement du contenu du microagent.", + "tr": "Mikro ajan içeriği yüklenirken hata oluştu.", + "de": "Fehler beim Laden des Microagent-Inhalts.", + "uk": "Помилка під час завантаження вмісту мікроагента." } } diff --git a/frontend/src/types/microagent-management.tsx b/frontend/src/types/microagent-management.tsx index 6d7752728c..d26ba5fe62 100644 --- a/frontend/src/types/microagent-management.tsx +++ b/frontend/src/types/microagent-management.tsx @@ -4,11 +4,6 @@ export type TabType = "personal" | "repositories" | "organizations"; export interface RepositoryMicroagent { name: string; - type: "repo" | "knowledge"; - content: string; - triggers: string[]; - inputs: string[]; - tools: string[]; created_at: string; git_provider: string; path: string; diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts index 7a113a8a23..134f9237f4 100644 --- a/frontend/src/utils/utils.ts +++ b/frontend/src/utils/utils.ts @@ -207,3 +207,22 @@ export const constructMicroagentUrl = ( return ""; } }; + +/** + * Extract repository owner, repo name, and file path from repository and microagent data + * @param selectedRepository The selected repository object with full_name property + * @param microagent The microagent object with path property + * @returns Object containing owner, repo, and filePath + * + * @example + * const { owner, repo, filePath } = extractRepositoryInfo(selectedRepository, microagent); + */ +export const extractRepositoryInfo = ( + selectedRepository: { full_name?: string } | null | undefined, + microagent: { path?: string } | null | undefined, +) => { + const [owner, repo] = selectedRepository?.full_name?.split("/") || []; + const filePath = microagent?.path || ""; + + return { owner, repo, filePath }; +};