diff --git a/apps/commons-app/app/studio/[tab]/page.tsx b/apps/commons-app/app/studio/[tab]/page.tsx
index 4ae1f97c..237cd518 100644
--- a/apps/commons-app/app/studio/[tab]/page.tsx
+++ b/apps/commons-app/app/studio/[tab]/page.tsx
@@ -228,7 +228,7 @@ const StudioPage: NextPage = () => {
case "tools":
return "Create new tool";
case "tasks":
- return "Create new task";
+ return "Create new scheduled task";
case "workflows":
return "Create new workflow";
case "skills":
@@ -249,7 +249,7 @@ const StudioPage: NextPage = () => {
};
case "tasks":
return {
- title: "Tasks",
+ title: "Scheduled tasks",
description:
"Schedule one-off or recurring tasks for your agents to run automatically.",
};
diff --git a/apps/commons-app/app/studio/agents/[agent]/page.tsx b/apps/commons-app/app/studio/agents/[agent]/page.tsx
index ddcba42b..254a9e5b 100644
--- a/apps/commons-app/app/studio/agents/[agent]/page.tsx
+++ b/apps/commons-app/app/studio/agents/[agent]/page.tsx
@@ -9,7 +9,6 @@ import {
BarChart3,
Bot,
Brain,
- CalendarCheck,
Check,
CheckCircle2,
ChevronRight,
@@ -27,13 +26,14 @@ import {
Search,
Server,
Settings2,
- Sparkles,
TerminalSquare,
Wallet,
Wrench,
XCircle,
Zap,
} from "lucide-react";
+import { ClipboardClock } from "@/components/icons/clipboard-clock";
+import { SkillIcon } from "@/components/skills/skill-icon";
import { formatDistanceToNow } from "date-fns";
import {
Area,
@@ -120,9 +120,9 @@ const sections: Array<{ key: SectionKey; label: string; icon: typeof Bot }> = [
{ key: "new-session", label: "New session", icon: Plus },
{ key: "sessions", label: "Sessions", icon: MessageSquare },
{ key: "computer", label: "Computer", icon: Monitor },
- { key: "tasks", label: "Tasks", icon: CalendarCheck },
+ { key: "tasks", label: "Scheduled tasks", icon: ClipboardClock },
{ key: "tools", label: "Tools", icon: Wrench },
- { key: "skills", label: "Skills", icon: Sparkles },
+ { key: "skills", label: "Skills", icon: Zap },
{ key: "artifacts", label: "Artifacts", icon: FileText },
{ key: "observability", label: "Observability", icon: TerminalSquare },
{ key: "usage", label: "Usage", icon: BarChart3 },
@@ -1473,7 +1473,7 @@ function SkillsView({
) : visibleSkills.length === 0 ? (
-
+
{view === "assigned" ? "No skills enabled" : "No skills found"}
@@ -1500,9 +1500,7 @@ function SkillsView({
}
className="flex min-w-0 flex-1 items-start gap-3 text-left"
>
-
- {skill.icon || }
-
+
diff --git a/apps/commons-app/app/studio/skills/[skillId]/page.tsx b/apps/commons-app/app/studio/skills/[skillId]/page.tsx
index 4593a775..5e07d34f 100644
--- a/apps/commons-app/app/studio/skills/[skillId]/page.tsx
+++ b/apps/commons-app/app/studio/skills/[skillId]/page.tsx
@@ -2,10 +2,11 @@
import { use, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
-import { ArrowLeft, Globe, Lock, Sparkles } from "lucide-react";
+import { ArrowLeft, Globe, Lock } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
+import { SkillIcon } from "@/components/skills/skill-icon";
import type { Skill } from "@agent-commons/sdk";
export default function SkillDetailPage({
@@ -74,13 +75,7 @@ export default function SkillDetailPage({
-
- {skill.icon ? (
- {skill.icon}
- ) : (
-
- )}
-
+
{skill.name}
{skill.slug}
diff --git a/apps/commons-app/app/studio/tasks/[taskId]/page.tsx b/apps/commons-app/app/studio/tasks/[taskId]/page.tsx
index 092395e6..611afa33 100644
--- a/apps/commons-app/app/studio/tasks/[taskId]/page.tsx
+++ b/apps/commons-app/app/studio/tasks/[taskId]/page.tsx
@@ -258,7 +258,7 @@ export default function TaskDetailPage({
Task not found.
router.push("/studio/tasks")}>
- Back to Tasks
+ Back to scheduled tasks
);
diff --git a/apps/commons-app/components/agents/runtime-native-tooling.tsx b/apps/commons-app/components/agents/runtime-native-tooling.tsx
index 1ab4ae2a..a1a7fbb4 100644
--- a/apps/commons-app/components/agents/runtime-native-tooling.tsx
+++ b/apps/commons-app/components/agents/runtime-native-tooling.tsx
@@ -13,9 +13,9 @@ import {
Send,
Server,
ShieldCheck,
- Sparkles,
Unplug,
X,
+ Zap,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -1467,7 +1467,7 @@ export function RuntimeSkillsNote({
const meta = RUNTIME_NATIVE_TOOLING[key];
return (
);
diff --git a/apps/commons-app/components/copilot/floating-commons-copilot.tsx b/apps/commons-app/components/copilot/floating-commons-copilot.tsx
index bfc50fa5..89981251 100644
--- a/apps/commons-app/components/copilot/floating-commons-copilot.tsx
+++ b/apps/commons-app/components/copilot/floating-commons-copilot.tsx
@@ -47,7 +47,7 @@ const SCOPES = [
["agents", "Agents"],
["tools", "Tools"],
["skills", "Skills"],
- ["tasks", "Tasks"],
+ ["tasks", "Scheduled tasks"],
] as const;
const PANEL_WIDTH_KEY = "copilot-panel-width";
diff --git a/apps/commons-app/components/customize/customize-tabs.tsx b/apps/commons-app/components/customize/customize-tabs.tsx
index 2270d942..0fc49a1c 100644
--- a/apps/commons-app/components/customize/customize-tabs.tsx
+++ b/apps/commons-app/components/customize/customize-tabs.tsx
@@ -2,7 +2,7 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
-import { AppWindow, Sparkles } from "lucide-react";
+import { AppWindow, Zap } from "lucide-react";
import { cn } from "@/lib/utils";
const tabs = [
@@ -16,7 +16,7 @@ const tabs = [
label: "Skills",
href: "/studio/customize/skills",
segment: "/studio/customize/skills",
- icon: Sparkles,
+ icon: Zap,
},
] as const;
diff --git a/apps/commons-app/components/icons/clipboard-clock.tsx b/apps/commons-app/components/icons/clipboard-clock.tsx
new file mode 100644
index 00000000..68c3a146
--- /dev/null
+++ b/apps/commons-app/components/icons/clipboard-clock.tsx
@@ -0,0 +1,40 @@
+import { forwardRef } from "react";
+import type { LucideProps } from "lucide-react";
+
+/**
+ * `clipboard-clock` from Lucide, inlined because the pinned lucide-react
+ * (0.474) predates the icon. Same 24×24 geometry, props, and stroke defaults
+ * as every other Lucide glyph, so it sizes and colors identically alongside
+ * them.
+ *
+ * This is the single icon for scheduled tasks across the app.
+ */
+export const ClipboardClock = forwardRef
(
+ ({ size = 24, strokeWidth = 2, absoluteStrokeWidth, ...props }, ref) => (
+
+
+
+
+
+
+
+ )
+);
+
+ClipboardClock.displayName = "ClipboardClock";
diff --git a/apps/commons-app/components/landing/landing-sidebar.tsx b/apps/commons-app/components/landing/landing-sidebar.tsx
index b61664c5..88a821fb 100644
--- a/apps/commons-app/components/landing/landing-sidebar.tsx
+++ b/apps/commons-app/components/landing/landing-sidebar.tsx
@@ -2,18 +2,18 @@ import Image from "next/image";
import Link from "next/link";
import {
Bot,
- BriefcaseBusiness,
LibraryBig,
MoreHorizontal,
Search,
Wrench,
Workflow,
} from "lucide-react";
+import { ClipboardClock } from "@/components/icons/clipboard-clock";
const NAV_ITEMS = [
{ label: "Agents", icon: Bot, target: "/studio/agents" },
{ label: "Tools", icon: Wrench, target: "/studio/tools" },
- { label: "Tasks", icon: BriefcaseBusiness, target: "/studio/tasks" },
+ { label: "Scheduled tasks", icon: ClipboardClock, target: "/studio/tasks" },
{ label: "Workflows", icon: Workflow, target: "/studio/workflows" },
{ label: "Library", icon: LibraryBig, target: "/library" },
];
diff --git a/apps/commons-app/components/layout/dashboard-bar.tsx b/apps/commons-app/components/layout/dashboard-bar.tsx
index ec6b504d..97658991 100644
--- a/apps/commons-app/components/layout/dashboard-bar.tsx
+++ b/apps/commons-app/components/layout/dashboard-bar.tsx
@@ -3,14 +3,8 @@
import { FC, ReactNode } from "react";
import { useRouter } from "next/navigation";
-import {
- Bot,
- BriefcaseBusiness,
- LibraryBig,
- Wrench,
- Workflow,
- Settings2,
-} from "lucide-react";
+import { Bot, LibraryBig, Wrench, Workflow, Settings2 } from "lucide-react";
+import { ClipboardClock } from "@/components/icons/clipboard-clock";
import Link from "next/link";
import Image from "next/image";
import { SearchTrigger } from "@/components/search/search-trigger";
@@ -35,8 +29,8 @@ export const DashboardBar: FC = ({
{ key: "tools", label: "Tools", icon: Wrench, path: "/studio/tools" },
{
key: "tasks",
- label: "Tasks",
- icon: BriefcaseBusiness,
+ label: "Scheduled tasks",
+ icon: ClipboardClock,
path: "/studio/tasks",
},
{
diff --git a/apps/commons-app/components/layout/dashboard-side-bar.tsx b/apps/commons-app/components/layout/dashboard-side-bar.tsx
index 878ce708..355c6508 100644
--- a/apps/commons-app/components/layout/dashboard-side-bar.tsx
+++ b/apps/commons-app/components/layout/dashboard-side-bar.tsx
@@ -7,7 +7,6 @@ import Image from "next/image";
import { cn } from "@/lib/utils";
import {
Bot,
- BriefcaseBusiness,
PanelLeft,
PanelRight,
LibraryBig,
@@ -16,6 +15,7 @@ import {
Workflow,
Settings2,
} from "lucide-react";
+import { ClipboardClock } from "@/components/icons/clipboard-clock";
import { ScrollArea } from "@/components/ui/scroll-area";
import { DashboardBar } from "./dashboard-bar";
import { SidebarAccount } from "./sidebar-account";
@@ -160,9 +160,9 @@ export function DashboardSideBar({ username }: { username: string }) {
},
{
key: "tasks",
- icon: BriefcaseBusiness,
+ icon: ClipboardClock,
path: "/studio/tasks",
- label: "Tasks",
+ label: "Scheduled tasks",
},
{
key: "workflows",
diff --git a/apps/commons-app/components/search/global-search.tsx b/apps/commons-app/components/search/global-search.tsx
index ffcc0cfc..d4e69b18 100644
--- a/apps/commons-app/components/search/global-search.tsx
+++ b/apps/commons-app/components/search/global-search.tsx
@@ -15,7 +15,6 @@ import {
Bot,
MessageSquare,
Wrench,
- BriefcaseBusiness,
Workflow,
Earth,
LibraryBig,
@@ -23,6 +22,7 @@ import {
Wallet,
Loader2,
} from "lucide-react";
+import { ClipboardClock } from "@/components/icons/clipboard-clock";
import { useAuth } from "@/context/AuthContext";
import { normalizePrincipalId } from "@/lib/principal-id";
import { useAgents } from "@/hooks/use-agents";
@@ -38,7 +38,7 @@ interface GlobalSearchProps {
const NAV_ITEMS = [
{ label: "Agents", path: "/studio/agents", icon: Bot, keywords: "agents" },
{ label: "Tools", path: "/studio/tools", icon: Wrench, keywords: "tools integrations" },
- { label: "Tasks", path: "/studio/tasks", icon: BriefcaseBusiness, keywords: "tasks queue" },
+ { label: "Scheduled tasks", path: "/studio/tasks", icon: ClipboardClock, keywords: "tasks queue scheduled cron recurring" },
{ label: "Workflows", path: "/studio/workflows", icon: Workflow, keywords: "workflows automation" },
{ label: "Spaces", path: "/spaces", icon: Earth, keywords: "spaces rooms live" },
{ label: "Library", path: "/library", icon: LibraryBig, keywords: "library files documents collections" },
@@ -191,14 +191,14 @@ export function GlobalSearch({ open, onOpenChange }: GlobalSearchProps) {
{tasks.length > 0 && (
<>
-
+
{tasks.map((t) => (
go(`/studio/tasks/${t.taskId}`)}
>
-
+
{t.title}
))}
diff --git a/apps/commons-app/components/skills/skill-icon.tsx b/apps/commons-app/components/skills/skill-icon.tsx
new file mode 100644
index 00000000..d92036a4
--- /dev/null
+++ b/apps/commons-app/components/skills/skill-icon.tsx
@@ -0,0 +1,135 @@
+"use client";
+
+import type { ElementType } from "react";
+import {
+ BarChart3,
+ Bot,
+ Brain,
+ Calendar,
+ Code2,
+ Database,
+ FileSpreadsheet,
+ FileText,
+ FileType,
+ Folder,
+ Github,
+ Globe,
+ Image,
+ Layers,
+ Link2,
+ Mail,
+ MessageSquare,
+ Monitor,
+ PanelsTopLeft,
+ PenLine,
+ Presentation,
+ Search,
+ Send,
+ Settings2,
+ Table2,
+ Terminal,
+ Users,
+ Video,
+ Workflow,
+ Wrench,
+ Zap,
+} from "lucide-react";
+import { cn } from "@/lib/utils";
+
+/**
+ * Skills store `icon` as either a Lucide icon name (platform skills seed
+ * kebab-case names like `panels-top-left`) or a single emoji (what the create
+ * form asks users for). Anything unrecognised falls back to the Zap mark
+ * rather than leaking a raw slug into the layout.
+ */
+const lucideByName: Record = {
+ "bar-chart-3": BarChart3,
+ bot: Bot,
+ brain: Brain,
+ calendar: Calendar,
+ "code-2": Code2,
+ code: Code2,
+ database: Database,
+ "file-spreadsheet": FileSpreadsheet,
+ "file-text": FileText,
+ "file-type": FileType,
+ folder: Folder,
+ github: Github,
+ globe: Globe,
+ image: Image,
+ layers: Layers,
+ "link-2": Link2,
+ mail: Mail,
+ "message-square": MessageSquare,
+ monitor: Monitor,
+ "panels-top-left": PanelsTopLeft,
+ "pen-line": PenLine,
+ presentation: Presentation,
+ search: Search,
+ send: Send,
+ "settings-2": Settings2,
+ "table-2": Table2,
+ terminal: Terminal,
+ users: Users,
+ video: Video,
+ workflow: Workflow,
+ wrench: Wrench,
+ zap: Zap,
+};
+
+/** `PanelsTopLeft` / `panelsTopLeft` / `Panels Top Left` → `panels-top-left`. */
+function toKebabCase(value: string) {
+ return value
+ .trim()
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
+ .replace(/[\s_]+/g, "-")
+ .toLowerCase();
+}
+
+/** Emoji and other pictographs are safe to render as text; slugs are not. */
+function isGlyph(value: string) {
+ const glyph = value.trim();
+ if (!glyph || /[a-zA-Z0-9]/.test(glyph)) return false;
+ return Array.from(glyph).length <= 2;
+}
+
+const sizeStyles = {
+ sm: { chip: "h-7 w-7 rounded-md", glyph: "text-sm", lucide: "h-3.5 w-3.5" },
+ md: { chip: "h-9 w-9 rounded-lg", glyph: "text-base", lucide: "h-4 w-4" },
+ lg: { chip: "h-11 w-11 rounded-xl", glyph: "text-lg", lucide: "h-5 w-5" },
+} as const;
+
+export function SkillIcon({
+ icon,
+ size = "md",
+ className,
+}: {
+ icon?: string | null;
+ size?: keyof typeof sizeStyles;
+ className?: string;
+}) {
+ const styles = sizeStyles[size];
+ const raw = icon?.trim() ?? "";
+ const Lucide = raw ? lucideByName[toKebabCase(raw)] : undefined;
+ const glyph = !Lucide && isGlyph(raw) ? raw : null;
+ const Fallback = Zap;
+
+ return (
+
+ {glyph ? (
+ {glyph}
+ ) : Lucide ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/apps/commons-app/components/skills/skills-marketplace-view.tsx b/apps/commons-app/components/skills/skills-marketplace-view.tsx
index 93632265..014f3a6c 100644
--- a/apps/commons-app/components/skills/skills-marketplace-view.tsx
+++ b/apps/commons-app/components/skills/skills-marketplace-view.tsx
@@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from "react";
import {
Loader2,
- Sparkles,
+ Zap,
Search,
Trash2,
Globe,
@@ -35,6 +35,7 @@ import {
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
+import { SkillIcon } from "@/components/skills/skill-icon";
import type { Skill } from "@agent-commons/sdk";
interface SkillsMarketplaceViewProps {
@@ -115,22 +116,39 @@ function SkillCard({
const enabledAgents = (skill.assignedAgents ?? []).filter(
(assignment) => assignment.isEnabled
);
+ const assignmentLabel = enabledAgents.length
+ ? `Available to ${enabledAgents
+ .slice(0, 2)
+ .map((assignment) => assignment.agentName)
+ .join(", ")}${
+ enabledAgents.length > 2 ? ` +${enabledAgents.length - 2}` : ""
+ }`
+ : "Not assigned to an agent";
return (
onOpen(skill.skillId)}
+ onKeyDown={(event) => {
+ if (event.key === "Enter" || event.key === " ") {
+ event.preventDefault();
+ onOpen(skill.skillId);
+ }
+ }}
>
-
-
- {skill.icon || (
-
- )}
-
-
+
+
+
+
+ {skill.name}
+
+
+ {skill.description}
+
+
+
{
event.stopPropagation();
onDelete(skill.skillId);
}}
- aria-label="Delete skill"
+ aria-label={`Delete ${skill.name}`}
>
@@ -152,41 +170,26 @@ function SkillCard({
-
- {skill.name}
-
-
- {skill.description}
-
-
-
{
event.stopPropagation();
onManageAgents(skill);
}}
+ title={assignmentLabel}
>
-
-
- {enabledAgents.length
- ? `Available to ${enabledAgents
- .slice(0, 2)
- .map((assignment) => assignment.agentName)
- .join(", ")}${
- enabledAgents.length > 2 ? ` +${enabledAgents.length - 2}` : ""
- }`
- : "Not assigned to an agent"}
-
-
+
+
{assignmentLabel}
+
-
-
+
+
{skill.tags.slice(0, 3).map((tag) => (
{tag}
@@ -194,14 +197,14 @@ function SkillCard({
{skill.tags.length > 3 && (
+{skill.tags.length - 3}
)}
-
- {skill.usageCount} uses
+
+ {skill.usageCount} {skill.usageCount === 1 ? "use" : "uses"}
@@ -612,23 +615,25 @@ export function SkillsMarketplaceView({
) : filtered.length === 0 ? (
-
-
-
+
+
+
{search
? "No skills match your search"
: tab === "mine"
? "No skills yet"
: "No platform skills"}
- {!search && tab === "mine" && (
-
- Create your first skill to add custom capabilities to your agents
-
- )}
+
+ {search
+ ? "Try a different name, description, or tag."
+ : tab === "mine"
+ ? "Create your first skill to add custom capabilities to your agents."
+ : "Platform skills will appear here once they are published."}
+
) : (
-
+
{filtered.map((skill) => (