diff --git a/apps/web/src/components/common/tool-card.tsx b/apps/web/src/components/common/tool-card.tsx
index 79b9a52d..32a817ab 100644
--- a/apps/web/src/components/common/tool-card.tsx
+++ b/apps/web/src/components/common/tool-card.tsx
@@ -1,8 +1,10 @@
import type { Tool } from "@ashim/shared";
-import { FileImage, Star } from "lucide-react";
+import { PYTHON_SIDECAR_TOOLS } from "@ashim/shared";
+import { Download, FileImage, Star } from "lucide-react";
import { Link } from "react-router-dom";
import { ICON_MAP } from "@/lib/icon-map";
import { cn } from "@/lib/utils";
+import { useFeaturesStore } from "@/stores/features-store";
interface ToolCardProps {
tool: Tool;
@@ -12,6 +14,10 @@ export function ToolCard({ tool }: ToolCardProps) {
const IconComponent =
(ICON_MAP[tool.icon] as React.ComponentType<{ className?: string }>) ?? FileImage;
+ const isAiTool = (PYTHON_SIDECAR_TOOLS as readonly string[]).includes(tool.id);
+ const isInstalled = useFeaturesStore((s) => s.isToolInstalled)(tool.id);
+ const showDownloadBadge = isAiTool && !isInstalled;
+
return (
);
diff --git a/apps/web/src/components/layout/tool-panel.tsx b/apps/web/src/components/layout/tool-panel.tsx
index a7641053..a00bf9d9 100644
--- a/apps/web/src/components/layout/tool-panel.tsx
+++ b/apps/web/src/components/layout/tool-panel.tsx
@@ -1,5 +1,6 @@
import { CATEGORIES, TOOLS } from "@ashim/shared";
import { useEffect, useMemo, useState } from "react";
+import { useFeaturesStore } from "@/stores/features-store";
import { useSettingsStore } from "@/stores/settings-store";
import { SearchBar } from "../common/search-bar";
import { ToolCard } from "../common/tool-card";
@@ -7,11 +8,16 @@ import { ToolCard } from "../common/tool-card";
export function ToolPanel() {
const [search, setSearch] = useState("");
const { disabledTools, experimentalEnabled, loaded, fetch } = useSettingsStore();
+ const fetchFeatures = useFeaturesStore((s) => s.fetch);
useEffect(() => {
fetch();
}, [fetch]);
+ useEffect(() => {
+ fetchFeatures();
+ }, [fetchFeatures]);
+
const visibleTools = useMemo(() => {
if (!loaded) return [];
return TOOLS.filter((t) => {
diff --git a/apps/web/src/pages/fullscreen-grid-page.tsx b/apps/web/src/pages/fullscreen-grid-page.tsx
index dcca7ff7..d2cf96dc 100644
--- a/apps/web/src/pages/fullscreen-grid-page.tsx
+++ b/apps/web/src/pages/fullscreen-grid-page.tsx
@@ -7,6 +7,7 @@ import { GemLogo } from "@/components/common/gem-logo";
import { apiGet } from "@/lib/api";
import { ICON_MAP } from "@/lib/icon-map";
import { cn } from "@/lib/utils";
+import { useFeaturesStore } from "@/stores/features-store";
export function FullscreenGridPage() {
const [search, setSearch] = useState("");
@@ -14,6 +15,11 @@ export function FullscreenGridPage() {
const navigate = useNavigate();
const [disabledTools, setDisabledTools] = useState([]);
const [experimentalEnabled, setExperimentalEnabled] = useState(false);
+ const fetchFeatures = useFeaturesStore((s) => s.fetch);
+
+ useEffect(() => {
+ fetchFeatures();
+ }, [fetchFeatures]);
useEffect(() => {
apiGet<{ settings: Record }>("/v1/settings")