From 3cf7e77872a5a6459e9e2a5ed5e6fcd7030decba Mon Sep 17 00:00:00 2001
From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com>
Date: Thu, 9 Apr 2026 15:42:25 +0000
Subject: [PATCH] fix: pro plugin and free plugin confusion (#843)
---
apps/web/components/integrations-view.tsx | 3 +-
.../integrations/plugins-detail.tsx | 937 +++++++++++-------
2 files changed, 590 insertions(+), 350 deletions(-)
diff --git a/apps/web/components/integrations-view.tsx b/apps/web/components/integrations-view.tsx
index b192c73f..a2304023 100644
--- a/apps/web/components/integrations-view.tsx
+++ b/apps/web/components/integrations-view.tsx
@@ -48,8 +48,7 @@ const cards: IntegrationCardDef[] = [
id: "plugins",
title: "Plugins",
description:
- "Claude Code, OpenCode, Hermes, and other AI tool integrations",
- pro: true,
+ "Hermes on every plan; Claude Code, OpenCode, OpenClaw, and more with Pro",
icon: (
void }) {
+ return (
+
+
+
+
+
+
+
+
+ Unlock Pro plugins
+
+
+ Connect Claude Code, OpenCode, OpenClaw, Cursor, and more with a
+ Pro plan.
+
+
+
+
+
+ {[
+ {
+ icon: Brain,
+ title: "Context Retention",
+ desc: "AI remembers your preferences across sessions",
+ },
+ {
+ icon: Zap,
+ title: "Instant Recall",
+ desc: "Past decisions surface automatically when relevant",
+ },
+ {
+ icon: Key,
+ title: "Secure & Private",
+ desc: "Your data stays yours with encrypted storage",
+ },
+ ].map(({ icon: Icon, title, desc }) => (
+
+
+
+
+ {title}
+
+
+ {desc}
+
+
+
+ ))}
+
+
+
+ {Object.values(PLUGIN_CATALOG)
+ .filter((p) => !isFreeTierPlugin(p.id))
+ .map((plugin) => (
+
+
+
+ ))}
+
+ Claude Code, OpenCode, OpenClaw & more
+
+
+
+
+
+
+ )
+}
+
+function ConnectedPluginRow({
+ plugin,
+ info,
+ onRevoke,
+}: {
+ plugin: ConnectedPlugin
+ info: PluginInfo | undefined
+ onRevoke: (keyId: string) => void
+}) {
+ return (
+
+
+ {info && (
+
+
+
+ )}
+
+
+ {info?.name || plugin.pluginId}
+
+
+
+
+ Connected
+
+ {plugin.keyStart && (
+
+ {plugin.keyStart}...
+
+ )}
+
+
+
+
+
+ )
+}
+
+function PluginCard({
+ plugin,
+ pluginId,
+ isConnected,
+ isCurrentlyConnecting,
+ connectingPlugin,
+ needsProUpgrade,
+ onConnect,
+ onUpgrade,
+}: {
+ plugin: PluginInfo
+ pluginId: string
+ isConnected: boolean
+ isCurrentlyConnecting: boolean
+ connectingPlugin: string | null
+ needsProUpgrade: boolean
+ onConnect: (id: string) => void
+ onUpgrade: () => void
+}) {
+ return (
+
+
+
+
+
+
+
+
+ {plugin.name}
+
+ {isConnected && (
+
+ Connected
+
+ )}
+
+
+ {plugin.description}
+
+
+
+
+
+ {plugin.features.map((feature) => (
+ -
+
+
+ {feature}
+
+
+ ))}
+
+
+
+ {isConnected ? (
+
+ ) : needsProUpgrade ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
+
export function PluginsDetail() {
const { org } = useAuth()
const autumn = useCustomer()
@@ -178,6 +524,16 @@ export function PluginsDetail() {
[connectedPlugins],
)
+ const freeConnected = useMemo(
+ () => connectedPlugins.filter((p) => isFreeTierPlugin(p.pluginId)),
+ [connectedPlugins],
+ )
+
+ const proConnected = useMemo(
+ () => connectedPlugins.filter((p) => !isFreeTierPlugin(p.pluginId)),
+ [connectedPlugins],
+ )
+
const createPluginKeyMutation = useMutation({
mutationFn: async (pluginId: string) => {
const API_URL =
@@ -188,7 +544,9 @@ export function PluginsDetail() {
})
if (!res.ok) {
if (res.status === 403) {
- throw new Error("A Pro plan is required to connect plugins.")
+ throw new Error(
+ "This plugin requires a Pro plan. Hermes is available on the Free plan.",
+ )
}
const errorData = (await res.json().catch(() => ({}))) as {
message?: string
@@ -249,139 +607,62 @@ export function PluginsDetail() {
const isLoading = autumn.isLoading
const availablePlugins = pluginsData?.plugins ?? Object.keys(PLUGIN_CATALOG)
+ const freePluginIds = useMemo(() => {
+ const ids = new Set(
+ availablePlugins.filter(
+ (id) => PLUGIN_CATALOG[id] && isFreeTierPlugin(id),
+ ),
+ )
+ if (PLUGIN_CATALOG.hermes) ids.add("hermes")
+ return [...ids]
+ }, [availablePlugins])
+
+ const proPluginIds = useMemo(
+ () =>
+ availablePlugins.filter(
+ (id) => PLUGIN_CATALOG[id] && !isFreeTierPlugin(id),
+ ),
+ [availablePlugins],
+ )
+
+ const allCatalogPluginIds = useMemo(
+ () => availablePlugins.filter((id) => PLUGIN_CATALOG[id]),
+ [availablePlugins],
+ )
+
+ const showPaidAllInOne = !isLoading && hasProProduct
+
return (
<>
- {/* Marketing hero for free users */}
- {!hasProProduct && !isLoading && (
-
-
-
-
-
-
-
-
- Unlock Persistent Memory for Your Tools
-
-
- Upgrade to Pro to connect plugins and give your AI tools
- long-term memory
-
-
-
-
-
- {[
- {
- icon: Brain,
- title: "Context Retention",
- desc: "AI remembers your preferences across sessions",
- },
- {
- icon: Zap,
- title: "Instant Recall",
- desc: "Past decisions surface automatically when relevant",
- },
- {
- icon: Key,
- title: "Secure & Private",
- desc: "Your data stays yours with encrypted storage",
- },
- ].map(({ icon: Icon, title, desc }) => (
-
-
-
-
- {title}
-
-
- {desc}
-
-
-
- ))}
-
-
-
- {Object.values(PLUGIN_CATALOG).map((plugin) => (
-
-
-
- ))}
-
- Claude Code, OpenCode, OpenClaw, Hermes & more
-
-
-
-
-
-
- )}
-
-
- {/* Connected plugins */}
- {connectedPlugins.length > 0 && (
+ {showPaidAllInOne ? (
+
+ {connectedPlugins.length > 0 && (
+
+
+ Connected
+
+ {connectedPlugins.map((plugin) => (
+
+ ))}
+
+ )}
+
- Connected Plugins
+ {connectedPlugins.length > 0
+ ? "Add more plugins"
+ : "Available plugins"}
- {connectedPlugins.map((plugin) => {
- const info = PLUGIN_CATALOG[plugin.pluginId]
- return (
-
-
- {info && (
-
-
-
- )}
-
-
- {info?.name || plugin.pluginId}
-
-
-
-
- Connected
-
- {plugin.keyStart && (
-
- {plugin.keyStart}...
-
- )}
-
-
-
-
-
- )
- })}
-
- )}
-
- {/* Available plugins */}
-
-
- {connectedPlugins.length > 0
- ? "Add More Plugins"
- : "Available Plugins"}
-
-
- {availablePlugins.map((pluginId) => {
- const plugin = PLUGIN_CATALOG[pluginId]
- if (!plugin) return null
-
- const isConnected = connectedPluginIds.includes(pluginId)
- const isCurrentlyConnecting = connectingPlugin === pluginId
-
- return (
-
-
-
-
-
-
-
-
- {plugin.name}
-
- {isConnected && (
-
- Connected
-
- )}
-
-
- {plugin.description}
-
-
-
-
-
- {plugin.features.map((feature) => (
- -
-
-
- {feature}
-
-
- ))}
-
-
-
- {isConnected ? (
-
- ) : (
-
- )}
-
-
-
- )
- })}
+
+ {allCatalogPluginIds.map((pluginId) => {
+ const plugin = PLUGIN_CATALOG[pluginId]
+ if (!plugin) return null
+ const isConnected = connectedPluginIds.includes(pluginId)
+ const isCurrentlyConnecting = connectingPlugin === pluginId
+ return (
+
createPluginKeyMutation.mutate(id)}
+ onUpgrade={handleUpgrade}
+ />
+ )
+ })}
+
-
+ ) : (
+
+
+
+ Free plugins
+
+
+ Pro plugins
+
+
+
+
+
+ Included on every plan — connect with no upgrade.
+
+
+ {freeConnected.length > 0 && (
+
+
+ Connected
+
+ {freeConnected.map((plugin) => (
+
+ ))}
+
+ )}
+
+
+
+ {freeConnected.length > 0 ? "Add or manage" : "Available"}
+
+
+ {freePluginIds.map((pluginId) => {
+ const plugin = PLUGIN_CATALOG[pluginId]
+ if (!plugin) return null
+ const isConnected = connectedPluginIds.includes(pluginId)
+ const isCurrentlyConnecting = connectingPlugin === pluginId
+ return (
+
createPluginKeyMutation.mutate(id)}
+ onUpgrade={handleUpgrade}
+ />
+ )
+ })}
+
+
+
+
+
+ {!hasProProduct && !isLoading && (
+
+ )}
+
+ {proConnected.length > 0 && (
+
+
+ Connected
+
+ {proConnected.map((plugin) => (
+
+ ))}
+
+ )}
+
+
+
+ {proConnected.length > 0 ? "Add more" : "Available plugins"}
+
+
+ {proPluginIds.map((pluginId) => {
+ const plugin = PLUGIN_CATALOG[pluginId]
+ if (!plugin) return null
+ const isConnected = connectedPluginIds.includes(pluginId)
+ const isCurrentlyConnecting = connectingPlugin === pluginId
+ const needsProUpgrade = !hasProProduct
+ return (
+
createPluginKeyMutation.mutate(id)}
+ onUpgrade={handleUpgrade}
+ />
+ )
+ })}
+
+
+
+
+ )}
- {/* API Key modal */}