+
-
+ {isLoading ? (
+
+
+
+ {t("HOME$LOADING_REPOSITORIES")}
+
+
+ ) : (
+
+ )}
);
}
diff --git a/frontend/src/components/shared/git-provider-icon.tsx b/frontend/src/components/shared/git-provider-icon.tsx
new file mode 100644
index 0000000000..ada62208af
--- /dev/null
+++ b/frontend/src/components/shared/git-provider-icon.tsx
@@ -0,0 +1,16 @@
+import { FaBitbucket, FaGithub, FaGitlab } from "react-icons/fa6";
+import { Provider } from "#/types/settings";
+
+interface GitProviderIconProps {
+ gitProvider: Provider;
+}
+
+export function GitProviderIcon({ gitProvider }: GitProviderIconProps) {
+ return (
+ <>
+ {gitProvider === "github" &&
}
+ {gitProvider === "gitlab" &&
}
+ {gitProvider === "bitbucket" &&
}
+ >
+ );
+}
diff --git a/frontend/src/hooks/query/use-repository-microagents.ts b/frontend/src/hooks/query/use-repository-microagents.ts
new file mode 100644
index 0000000000..a7a7ae1ee0
--- /dev/null
+++ b/frontend/src/hooks/query/use-repository-microagents.ts
@@ -0,0 +1,11 @@
+import { useQuery } from "@tanstack/react-query";
+import OpenHands from "#/api/open-hands";
+
+export const useRepositoryMicroagents = (owner: string, repo: string) =>
+ useQuery({
+ queryKey: ["repository", "microagents", owner, repo],
+ queryFn: () => OpenHands.getRepositoryMicroagents(owner, repo),
+ enabled: !!owner && !!repo,
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ gcTime: 1000 * 60 * 15, // 15 minutes
+ });
diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts
index 3bad412ff7..d125e32f66 100644
--- a/frontend/src/i18n/declaration.ts
+++ b/frontend/src/i18n/declaration.ts
@@ -708,4 +708,7 @@ export enum I18nKey {
COMMON$RUN_TEST = "COMMON$RUN_TEST",
COMMON$RUN_APP = "COMMON$RUN_APP",
COMMON$LEARN_FILE_STRUCTURE = "COMMON$LEARN_FILE_STRUCTURE",
+ MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS",
+ MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS",
+ MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS = "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS",
}
diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json
index b0b5be3b91..9c8c89da77 100644
--- a/frontend/src/i18n/translation.json
+++ b/frontend/src/i18n/translation.json
@@ -11326,5 +11326,53 @@
"tr": "Dosya yapısını öğren",
"de": "Dateistruktur lernen",
"uk": "Вивчити структуру файлів"
+ },
+ "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_USER_LEVEL_MICROAGENTS": {
+ "en": "You do not have user-level microagents",
+ "ja": "ユーザーレベルのマイクロエージェントがありません",
+ "zh-CN": "您没有用户级微代理",
+ "zh-TW": "您沒有使用者層級的微代理",
+ "ko-KR": "사용자 수준의 마이크로에이전트가 없습니다",
+ "no": "Du har ikke mikroagenter på brukernivå",
+ "it": "Non hai microagenti a livello utente",
+ "pt": "Você não possui microagentes de nível de usuário",
+ "es": "No tienes microagentes a nivel de usuario",
+ "ar": "ليس لديك وكلاء دقيقون على مستوى المستخدم",
+ "fr": "Vous n'avez pas de microagents au niveau utilisateur",
+ "tr": "Kullanıcı düzeyinde mikro ajanınız yok",
+ "de": "Sie haben keine Mikroagenten auf Benutzerebene",
+ "uk": "У вас немає мікроагентів на рівні користувача"
+ },
+ "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_MICROAGENTS": {
+ "en": "You do not have microagents",
+ "ja": "マイクロエージェントがありません",
+ "zh-CN": "您没有微代理",
+ "zh-TW": "您沒有微代理",
+ "ko-KR": "마이크로에이전트가 없습니다",
+ "no": "Du har ingen mikroagenter",
+ "it": "Non hai microagenti",
+ "pt": "Você não possui microagentes",
+ "es": "No tienes microagentes",
+ "ar": "ليس لديك وكلاء دقيقون",
+ "fr": "Vous n'avez pas de microagents",
+ "tr": "Mikro ajanınız yok",
+ "de": "Sie haben keine Mikroagenten",
+ "uk": "У вас немає мікроагентів"
+ },
+ "MICROAGENT_MANAGEMENT$YOU_DO_NOT_HAVE_ORGANIZATION_LEVEL_MICROAGENTS": {
+ "en": "You do not have organization-level microagents",
+ "ja": "組織レベルのマイクロエージェントがありません",
+ "zh-CN": "您没有组织级微代理",
+ "zh-TW": "您沒有組織層級的微代理",
+ "ko-KR": "조직 수준의 마이크로에이전트가 없습니다",
+ "no": "Du har ikke mikroagenter på organisasjonsnivå",
+ "it": "Non hai microagenti a livello organizzazione",
+ "pt": "Você não possui microagentes de nível organizacional",
+ "es": "No tienes microagentes a nivel de organización",
+ "ar": "ليس لديك وكلاء دقيقون على مستوى المؤسسة",
+ "fr": "Vous n'avez pas de microagents au niveau organisation",
+ "tr": "Organizasyon düzeyinde mikro ajanınız yok",
+ "de": "Sie haben keine Mikroagenten auf Organisationsebene",
+ "uk": "У вас немає мікроагентів на рівні організації"
}
}
diff --git a/frontend/src/state/microagent-management-slice.tsx b/frontend/src/state/microagent-management-slice.tsx
index e6395aff4e..4856c394fb 100644
--- a/frontend/src/state/microagent-management-slice.tsx
+++ b/frontend/src/state/microagent-management-slice.tsx
@@ -1,4 +1,5 @@
import { createSlice } from "@reduxjs/toolkit";
+import { GitRepository } from "#/types/git";
export const microagentManagementSlice = createSlice({
name: "microagentManagement",
@@ -6,6 +7,9 @@ export const microagentManagementSlice = createSlice({
selectedMicroagent: null,
addMicroagentModalVisible: false,
selectedRepository: null,
+ personalRepositories: [] as GitRepository[],
+ organizationRepositories: [] as GitRepository[],
+ repositories: [] as GitRepository[],
},
reducers: {
setSelectedMicroagent: (state, action) => {
@@ -17,6 +21,15 @@ export const microagentManagementSlice = createSlice({
setSelectedRepository: (state, action) => {
state.selectedRepository = action.payload;
},
+ setPersonalRepositories: (state, action) => {
+ state.personalRepositories = action.payload;
+ },
+ setOrganizationRepositories: (state, action) => {
+ state.organizationRepositories = action.payload;
+ },
+ setRepositories: (state, action) => {
+ state.repositories = action.payload;
+ },
},
});
@@ -24,6 +37,9 @@ export const {
setSelectedMicroagent,
setAddMicroagentModalVisible,
setSelectedRepository,
+ setPersonalRepositories,
+ setOrganizationRepositories,
+ setRepositories,
} = microagentManagementSlice.actions;
export default microagentManagementSlice.reducer;
diff --git a/frontend/src/types/git.d.ts b/frontend/src/types/git.d.ts
index 758399e503..a5f1daa483 100644
--- a/frontend/src/types/git.d.ts
+++ b/frontend/src/types/git.d.ts
@@ -30,6 +30,7 @@ interface GitRepository {
stargazers_count?: number;
link_header?: string;
pushed_at?: string;
+ owner_type?: "user" | "organization";
}
interface GitHubCommit {
diff --git a/frontend/src/types/microagent-management.tsx b/frontend/src/types/microagent-management.tsx
new file mode 100644
index 0000000000..970e71e948
--- /dev/null
+++ b/frontend/src/types/microagent-management.tsx
@@ -0,0 +1,12 @@
+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;
+}
diff --git a/frontend/src/utils/constants.ts b/frontend/src/utils/constants.ts
index 718be6a0c8..36ac68ad43 100644
--- a/frontend/src/utils/constants.ts
+++ b/frontend/src/utils/constants.ts
@@ -28,3 +28,12 @@ export const JSON_VIEW_THEME = {
base0E: "#c792ea", // keywords, purple
base0F: "#ff5370", // deprecated, red
};
+
+export const DOCUMENTATION_URL = {
+ MICROAGENTS: {
+ MICROAGENTS_OVERVIEW:
+ "https://docs.all-hands.dev/usage/prompting/microagents-overview",
+ ORGANIZATION_AND_USER_MICROAGENTS:
+ "https://docs.all-hands.dev/usage/prompting/microagents-org",
+ },
+};
diff --git a/frontend/src/utils/format-time-delta.ts b/frontend/src/utils/format-time-delta.ts
index 3bd43f14f5..8f2425a234 100644
--- a/frontend/src/utils/format-time-delta.ts
+++ b/frontend/src/utils/format-time-delta.ts
@@ -26,3 +26,19 @@ export const formatTimeDelta = (date: Date) => {
if (months < 12) return `${months}mo`;
return `${years}y`;
};
+
+/**
+ * Formats a date into a MM/DD/YYYY string format.
+ * @param date The date to format
+ * @returns A string in MM/DD/YYYY format
+ *
+ * @example
+ * formatDateMMDDYYYY(new Date("2025-05-30T00:15:08")); // "05/30/2025"
+ * formatDateMMDDYYYY(new Date("2024-12-25T10:30:00")); // "12/25/2024"
+ */
+export const formatDateMMDDYYYY = (date: Date) =>
+ date.toLocaleDateString("en-US", {
+ month: "2-digit",
+ day: "2-digit",
+ year: "numeric",
+ });
diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts
index 6fda98bde6..182a563ffa 100644
--- a/frontend/src/utils/utils.ts
+++ b/frontend/src/utils/utils.ts
@@ -1,5 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
+import { Provider } from "#/types/settings";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@@ -102,3 +103,16 @@ export const formatTimestamp = (timestamp: string) =>
minute: "2-digit",
second: "2-digit",
});
+
+export const getGitProviderBaseUrl = (gitProvider: Provider): string => {
+ switch (gitProvider) {
+ case "github":
+ return "https://github.com";
+ case "gitlab":
+ return "https://gitlab.com";
+ case "bitbucket":
+ return "https://bitbucket.org";
+ default:
+ return "";
+ }
+};