diff --git a/apps/code/src/renderer/components/HeaderRow.tsx b/apps/code/src/renderer/components/HeaderRow.tsx
index 2e36918e6..86872be20 100644
--- a/apps/code/src/renderer/components/HeaderRow.tsx
+++ b/apps/code/src/renderer/components/HeaderRow.tsx
@@ -4,6 +4,7 @@ import { CloudGitInteractionHeader } from "@features/git-interaction/components/
import { GitInteractionHeader } from "@features/git-interaction/components/GitInteractionHeader";
import { SidebarTrigger } from "@features/sidebar/components/SidebarTrigger";
import { useSidebarStore } from "@features/sidebar/stores/sidebarStore";
+import { SkillButtonsMenu } from "@features/skill-buttons/components/SkillButtonsMenu";
import { useWorkspace } from "@features/workspace/hooks/useWorkspace";
import { Box, Flex } from "@radix-ui/themes";
import { useHeaderStore } from "@stores/headerStore";
@@ -110,6 +111,9 @@ export function HeaderRow() {
overflow: "hidden",
}}
>
+
+
+
{activeWorkspace &&
(activeWorkspace.branchName || activeWorkspace.baseBranch) && (
diff --git a/apps/code/src/renderer/features/sessions/components/ConversationView.tsx b/apps/code/src/renderer/features/sessions/components/ConversationView.tsx
index 926d4bb89..bb8d63be7 100644
--- a/apps/code/src/renderer/features/sessions/components/ConversationView.tsx
+++ b/apps/code/src/renderer/features/sessions/components/ConversationView.tsx
@@ -7,6 +7,7 @@ import {
useSessionForTask,
} from "@features/sessions/stores/sessionStore";
import { useSettingsStore } from "@features/settings/stores/settingsStore";
+import { SkillButtonActionMessage } from "@features/skill-buttons/components/SkillButtonActionMessage";
import { ArrowDown, XCircle } from "@phosphor-icons/react";
import { Box, Button, Flex, Text } from "@radix-ui/themes";
import type { AcpMessage } from "@shared/types/session-events";
@@ -167,6 +168,8 @@ export function ConversationView({
);
case "git_action":
return
;
+ case "skill_button_action":
+ return
;
case "session_update":
return (
();
const gitAction = parseGitActionMessage(userContent);
+ const skillButtonId = extractSkillButtonId(userPrompt.blocks);
const childItems = new Map();
const context: TurnContext = {
@@ -270,6 +276,12 @@ function handlePromptRequest(
id: `${turnId}-git-action`,
actionType: gitAction.actionType,
});
+ } else if (skillButtonId) {
+ b.items.push({
+ type: "skill_button_action",
+ id: `${turnId}-skill-action`,
+ buttonId: skillButtonId,
+ });
} else {
b.items.push({
type: "user_message",
@@ -520,16 +532,17 @@ function ensureImplicitTurn(b: ItemBuilder, ts: number) {
function extractUserPrompt(params: unknown): {
content: string;
attachments: UserMessageAttachment[];
+ blocks: ContentBlock[];
} {
const p = params as { prompt?: ContentBlock[] };
if (!p?.prompt?.length) {
- return { content: "", attachments: [] };
+ return { content: "", attachments: [], blocks: [] };
}
const { text, attachments } = extractPromptDisplayContent(p.prompt, {
filterHidden: true,
});
- return { content: text, attachments };
+ return { content: text, attachments, blocks: p.prompt };
}
function getParentToolCallId(update: SessionUpdate): string | undefined {
diff --git a/apps/code/src/renderer/features/sessions/service/service.ts b/apps/code/src/renderer/features/sessions/service/service.ts
index 41e1ba26a..c69dcc3e5 100644
--- a/apps/code/src/renderer/features/sessions/service/service.ts
+++ b/apps/code/src/renderer/features/sessions/service/service.ts
@@ -28,6 +28,7 @@ import {
} from "@features/sessions/stores/sessionStore";
import { useSettingsStore } from "@features/settings/stores/settingsStore";
import { taskViewedApi } from "@features/sidebar/hooks/useTaskViewed";
+import { extractSkillButtonId } from "@features/skill-buttons/prompts";
import { isNotification, POSTHOG_NOTIFICATIONS } from "@posthog/agent";
import {
getAvailableCodexModes,
@@ -1331,11 +1332,19 @@ export class SessionService {
pausedDurationMs: 0,
});
- sessionStoreSetters.appendOptimisticItem(session.taskRunId, {
- type: "user_message",
- content: promptText,
- timestamp: Date.now(),
- });
+ const skillButtonId = extractSkillButtonId(blocks);
+ if (skillButtonId) {
+ sessionStoreSetters.appendOptimisticItem(session.taskRunId, {
+ type: "skill_button_action",
+ buttonId: skillButtonId,
+ });
+ } else {
+ sessionStoreSetters.appendOptimisticItem(session.taskRunId, {
+ type: "user_message",
+ content: promptText,
+ timestamp: Date.now(),
+ });
+ }
try {
const result = await trpcClient.agent.prompt.mutate({
diff --git a/apps/code/src/renderer/features/sessions/stores/sessionStore.ts b/apps/code/src/renderer/features/sessions/stores/sessionStore.ts
index 23c9334a9..f177b927a 100644
--- a/apps/code/src/renderer/features/sessions/stores/sessionStore.ts
+++ b/apps/code/src/renderer/features/sessions/stores/sessionStore.ts
@@ -6,6 +6,7 @@ import type {
SessionConfigSelectOptions,
} from "@agentclientprotocol/sdk";
import type { ExecutionMode, TaskRunStatus } from "@shared/types";
+import type { SkillButtonId } from "@shared/types/analytics";
import type { AcpMessage } from "@shared/types/session-events";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
@@ -25,12 +26,18 @@ export interface QueuedMessage {
export type { TaskRunStatus };
-export type OptimisticItem = {
- type: "user_message";
- id: string;
- content: string;
- timestamp: number;
-};
+export type OptimisticItem =
+ | {
+ type: "user_message";
+ id: string;
+ content: string;
+ timestamp: number;
+ }
+ | {
+ type: "skill_button_action";
+ id: string;
+ buttonId: SkillButtonId;
+ };
export interface AgentSession {
taskRunId: string;
@@ -379,13 +386,17 @@ export const sessionStoreSetters = {
appendOptimisticItem: (
taskRunId: string,
- item: Omit,
+ item: OptimisticItem extends infer T
+ ? T extends { id: string }
+ ? Omit
+ : never
+ : never,
): void => {
const id = `optimistic-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
useSessionStore.setState((state) => {
const session = state.sessions[taskRunId];
if (session) {
- session.optimisticItems.push({ ...item, id });
+ session.optimisticItems.push({ ...item, id } as OptimisticItem);
}
});
},
diff --git a/apps/code/src/renderer/features/sessions/utils/sendPromptToAgent.ts b/apps/code/src/renderer/features/sessions/utils/sendPromptToAgent.ts
index b6765b0b9..a3cd2a3d5 100644
--- a/apps/code/src/renderer/features/sessions/utils/sendPromptToAgent.ts
+++ b/apps/code/src/renderer/features/sessions/utils/sendPromptToAgent.ts
@@ -1,3 +1,4 @@
+import type { ContentBlock } from "@agentclientprotocol/sdk";
import { useReviewNavigationStore } from "@features/code-review/stores/reviewNavigationStore";
import { DEFAULT_TAB_IDS } from "@features/panels/constants/panelConstants";
import { usePanelLayoutStore } from "@features/panels/store/panelLayoutStore";
@@ -8,7 +9,10 @@ import { getSessionService } from "@features/sessions/service/service";
* Sends a prompt to the agent session for a task, collapses the review
* panel to split mode if expanded, and switches to the logs/chat tab.
*/
-export function sendPromptToAgent(taskId: string, prompt: string): void {
+export function sendPromptToAgent(
+ taskId: string,
+ prompt: string | ContentBlock[],
+): void {
getSessionService().sendPrompt(taskId, prompt);
const { getReviewMode, setReviewMode } = useReviewNavigationStore.getState();
diff --git a/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.stories.tsx b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.stories.tsx
new file mode 100644
index 000000000..23a23beec
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.stories.tsx
@@ -0,0 +1,49 @@
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { SkillButtonActionMessage } from "./SkillButtonActionMessage";
+
+const meta: Meta = {
+ title: "Skill Buttons/SkillButtonActionMessage",
+ component: SkillButtonActionMessage,
+ parameters: {
+ layout: "centered",
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const AddAnalytics: Story = {
+ args: {
+ buttonId: "add-analytics",
+ },
+};
+
+export const CreateFeatureFlag: Story = {
+ args: {
+ buttonId: "create-feature-flags",
+ },
+};
+
+export const RunExperiment: Story = {
+ args: {
+ buttonId: "run-experiment",
+ },
+};
+
+export const AddErrorTracking: Story = {
+ args: {
+ buttonId: "add-error-tracking",
+ },
+};
+
+export const InstrumentLlmCalls: Story = {
+ args: {
+ buttonId: "instrument-llm-calls",
+ },
+};
+
+export const AddLogging: Story = {
+ args: {
+ buttonId: "add-logging",
+ },
+};
diff --git a/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.tsx b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.tsx
new file mode 100644
index 000000000..0b99db9fe
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonActionMessage.tsx
@@ -0,0 +1,30 @@
+import {
+ SKILL_BUTTONS,
+ type SkillButtonId,
+} from "@features/skill-buttons/prompts";
+
+interface SkillButtonActionMessageProps {
+ buttonId: SkillButtonId;
+}
+
+export function SkillButtonActionMessage({
+ buttonId,
+}: SkillButtonActionMessageProps) {
+ const { Icon, color, actionTitle, actionDescription } =
+ SKILL_BUTTONS[buttonId];
+
+ return (
+
+
+
+
+ {actionTitle}
+
+ — {actionDescription}
+
+
+ );
+}
diff --git a/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.stories.tsx b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.stories.tsx
new file mode 100644
index 000000000..8541d8f48
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.stories.tsx
@@ -0,0 +1,18 @@
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { SkillButtonsMenu } from "./SkillButtonsMenu";
+
+const meta: Meta = {
+ title: "Skill Buttons/SkillButtonsMenu",
+ component: SkillButtonsMenu,
+ parameters: {
+ layout: "centered",
+ },
+ args: {
+ taskId: "storybook-task",
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.tsx b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.tsx
new file mode 100644
index 000000000..f52dcabc2
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/components/SkillButtonsMenu.tsx
@@ -0,0 +1,109 @@
+import { sendPromptToAgent } from "@features/sessions/utils/sendPromptToAgent";
+import {
+ buildSkillButtonPromptBlocks,
+ SKILL_BUTTON_ORDER,
+ SKILL_BUTTONS,
+ type SkillButton,
+ type SkillButtonId,
+} from "@features/skill-buttons/prompts";
+import { useSkillButtonsStore } from "@features/skill-buttons/stores/skillButtonsStore";
+import { CaretDown } from "@phosphor-icons/react";
+import {
+ Button,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@posthog/quill";
+import { ANALYTICS_EVENTS } from "@shared/types/analytics";
+import { track } from "@utils/analytics";
+
+interface SkillButtonsMenuProps {
+ taskId: string;
+}
+
+function SkillButtonIcon({ button }: { button: SkillButton }) {
+ const { Icon, color } = button;
+ return ;
+}
+
+export function SkillButtonsMenu({ taskId }: SkillButtonsMenuProps) {
+ const lastSelectedId = useSkillButtonsStore((s) => s.lastSelectedId);
+ const setLastSelectedId = useSkillButtonsStore((s) => s.setLastSelectedId);
+
+ const primaryButton = SKILL_BUTTONS[lastSelectedId];
+ const dropdownButtons = SKILL_BUTTON_ORDER.filter(
+ (id) => id !== lastSelectedId,
+ ).map((id) => SKILL_BUTTONS[id]);
+
+ const handleTrigger = (
+ buttonId: SkillButtonId,
+ source: "primary" | "dropdown",
+ ) => {
+ track(ANALYTICS_EVENTS.SKILL_BUTTON_TRIGGERED, {
+ task_id: taskId,
+ button_id: buttonId,
+ source,
+ });
+ setLastSelectedId(buttonId);
+ sendPromptToAgent(taskId, buildSkillButtonPromptBlocks(buttonId));
+ };
+
+ return (
+
+
+
+ handleTrigger(primaryButton.id, "primary")}
+ >
+
+ {primaryButton.label}
+
+ }
+ />
+ {primaryButton.tooltip}
+
+
+
+
+
+ }
+ />
+
+ {dropdownButtons.map((button) => (
+
+ handleTrigger(button.id, "dropdown")}
+ >
+
+ {button.label}
+
+ }
+ />
+ {button.tooltip}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/apps/code/src/renderer/features/skill-buttons/prompts.test.ts b/apps/code/src/renderer/features/skill-buttons/prompts.test.ts
new file mode 100644
index 000000000..de570e360
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/prompts.test.ts
@@ -0,0 +1,59 @@
+import type { ContentBlock } from "@agentclientprotocol/sdk";
+import { describe, expect, it } from "vitest";
+import {
+ buildSkillButtonPromptBlocks,
+ extractSkillButtonId,
+ SKILL_BUTTONS,
+} from "./prompts";
+
+describe("buildSkillButtonPromptBlocks", () => {
+ it("produces a text block carrying the button id under posthogCode meta", () => {
+ const [block] = buildSkillButtonPromptBlocks("add-analytics");
+ expect(block.type).toBe("text");
+ expect((block as { text: string }).text).toBe(
+ SKILL_BUTTONS["add-analytics"].prompt,
+ );
+ expect((block as { _meta?: unknown })._meta).toEqual({
+ posthogCode: { skillButtonId: "add-analytics" },
+ });
+ });
+});
+
+describe("extractSkillButtonId", () => {
+ it("round-trips through buildSkillButtonPromptBlocks", () => {
+ for (const id of Object.keys(SKILL_BUTTONS)) {
+ const blocks = buildSkillButtonPromptBlocks(
+ id as keyof typeof SKILL_BUTTONS,
+ );
+ expect(extractSkillButtonId(blocks)).toBe(id);
+ }
+ });
+
+ it("returns null for blocks with no meta", () => {
+ const blocks: ContentBlock[] = [{ type: "text", text: "hello" }];
+ expect(extractSkillButtonId(blocks)).toBeNull();
+ });
+
+ it("returns null when meta carries an unknown id", () => {
+ const blocks: ContentBlock[] = [
+ {
+ type: "text",
+ text: "hi",
+ _meta: { posthogCode: { skillButtonId: "unknown" } },
+ },
+ ];
+ expect(extractSkillButtonId(blocks)).toBeNull();
+ });
+
+ it("ignores plain text that happens to match a prompt string", () => {
+ const blocks: ContentBlock[] = [
+ { type: "text", text: SKILL_BUTTONS["add-analytics"].prompt },
+ ];
+ expect(extractSkillButtonId(blocks)).toBeNull();
+ });
+
+ it("handles undefined blocks", () => {
+ expect(extractSkillButtonId(undefined)).toBeNull();
+ expect(extractSkillButtonId([])).toBeNull();
+ });
+});
diff --git a/apps/code/src/renderer/features/skill-buttons/prompts.ts b/apps/code/src/renderer/features/skill-buttons/prompts.ts
new file mode 100644
index 000000000..4e68e5daa
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/prompts.ts
@@ -0,0 +1,144 @@
+import type { ContentBlock } from "@agentclientprotocol/sdk";
+import {
+ Broadcast,
+ ChartBar,
+ Flask,
+ type Icon,
+ Pulse,
+ ToggleRight,
+ Warning,
+} from "@phosphor-icons/react";
+import type { SkillButtonId } from "@shared/types/analytics";
+
+export type { SkillButtonId };
+
+export interface SkillButton {
+ id: SkillButtonId;
+ label: string;
+ prompt: string;
+ color: string;
+ Icon: Icon;
+ actionTitle: string;
+ actionDescription: string;
+ tooltip: string;
+}
+
+export const SKILL_BUTTONS: Record = {
+ "add-analytics": {
+ id: "add-analytics",
+ label: "Track events",
+ prompt: "/instrument-product-analytics",
+ color: "#2F80FA",
+ Icon: ChartBar,
+ actionTitle: "Adding analytics",
+ actionDescription: "to measure how this change performs in production.",
+ tooltip:
+ "Instrument PostHog events so you can measure this change in production",
+ },
+ "create-feature-flags": {
+ id: "create-feature-flags",
+ label: "Add feature flag",
+ prompt: "/instrument-feature-flags",
+ color: "#30ABC6",
+ Icon: ToggleRight,
+ actionTitle: "Creating a feature flag",
+ actionDescription:
+ "to roll this out safely and toggle it without a redeploy.",
+ tooltip:
+ "Gate this change behind a PostHog feature flag for a safe rollout",
+ },
+ "run-experiment": {
+ id: "run-experiment",
+ label: "Run experiment",
+ prompt:
+ "Set up a PostHog experiment for the feature in this task. Use the PostHog MCP to create the feature flag with control and test variants, then create the experiment in draft with a clear hypothesis and primary metric tied to the feature's success. Wire the variant into the code via posthog.getFeatureFlag. Only launch the experiment if the feature is already live in production — otherwise leave it in draft and tell me to launch it after this is merged and deployed.",
+ color: "#B62AD9",
+ Icon: Flask,
+ actionTitle: "Setting up an experiment",
+ actionDescription:
+ "with control and test variants tied to a primary metric, ready to launch once this ships.",
+ tooltip:
+ "Scaffold a PostHog A/B experiment with control and test variants tied to a primary metric",
+ },
+ "add-error-tracking": {
+ id: "add-error-tracking",
+ label: "Track errors",
+ prompt: "/instrument-error-tracking",
+ color: "#BF8113",
+ Icon: Warning,
+ actionTitle: "Adding error tracking",
+ actionDescription:
+ "so exceptions surface in PostHog with stack traces and source maps.",
+ tooltip:
+ "Capture exceptions in PostHog with stack traces so issues surface quickly in production",
+ },
+ "instrument-llm-calls": {
+ id: "instrument-llm-calls",
+ label: "Trace LLM calls",
+ prompt: "/instrument-llm-analytics",
+ color: "#B029D2",
+ Icon: Broadcast,
+ actionTitle: "Instrumenting LLM calls",
+ actionDescription:
+ "for visibility into prompts, tokens, latency, and costs.",
+ tooltip:
+ "Inspect traces, spans, latency, usage, and per-user costs for AI-powered features",
+ },
+ "add-logging": {
+ id: "add-logging",
+ label: "Capture logs",
+ prompt: "/instrument-logs",
+ color: "#C92474",
+ Icon: Pulse,
+ actionTitle: "Adding logging",
+ actionDescription:
+ "so structured log events flow into PostHog for inspection and debugging.",
+ tooltip:
+ "Capture structured application logs in PostHog for inspection and debugging",
+ },
+};
+
+export const SKILL_BUTTON_ORDER: SkillButtonId[] = [
+ "add-analytics",
+ "add-logging",
+ "add-error-tracking",
+ "instrument-llm-calls",
+ "create-feature-flags",
+ "run-experiment",
+];
+
+const SKILL_BUTTON_META_NAMESPACE = "posthogCode";
+const SKILL_BUTTON_META_FIELD = "skillButtonId";
+
+export function buildSkillButtonPromptBlocks(
+ buttonId: SkillButtonId,
+): ContentBlock[] {
+ return [
+ {
+ type: "text",
+ text: SKILL_BUTTONS[buttonId].prompt,
+ _meta: {
+ [SKILL_BUTTON_META_NAMESPACE]: {
+ [SKILL_BUTTON_META_FIELD]: buttonId,
+ },
+ },
+ },
+ ];
+}
+
+export function extractSkillButtonId(
+ blocks: ContentBlock[] | undefined,
+): SkillButtonId | null {
+ if (!blocks?.length) return null;
+ for (const block of blocks) {
+ const meta = (block as { _meta?: Record })._meta;
+ const namespace = meta?.[SKILL_BUTTON_META_NAMESPACE] as
+ | Record
+ | undefined;
+ const id = namespace?.[SKILL_BUTTON_META_FIELD];
+ if (typeof id === "string" && id in SKILL_BUTTONS) {
+ return id as SkillButtonId;
+ }
+ }
+ return null;
+}
diff --git a/apps/code/src/renderer/features/skill-buttons/stores/skillButtonsStore.ts b/apps/code/src/renderer/features/skill-buttons/stores/skillButtonsStore.ts
new file mode 100644
index 000000000..229854e73
--- /dev/null
+++ b/apps/code/src/renderer/features/skill-buttons/stores/skillButtonsStore.ts
@@ -0,0 +1,45 @@
+import {
+ SKILL_BUTTON_ORDER,
+ SKILL_BUTTONS,
+} from "@features/skill-buttons/prompts";
+import type { SkillButtonId } from "@shared/types/analytics";
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+
+interface SkillButtonsStoreState {
+ lastSelectedId: SkillButtonId;
+}
+
+interface SkillButtonsStoreActions {
+ setLastSelectedId: (id: SkillButtonId) => void;
+}
+
+type SkillButtonsStore = SkillButtonsStoreState & SkillButtonsStoreActions;
+
+const DEFAULT_PRIMARY: SkillButtonId = SKILL_BUTTON_ORDER[0];
+
+export const useSkillButtonsStore = create()(
+ persist(
+ (set) => ({
+ lastSelectedId: DEFAULT_PRIMARY,
+ setLastSelectedId: (lastSelectedId) => set({ lastSelectedId }),
+ }),
+ {
+ name: "skill-buttons-storage",
+ merge: (persisted, current) => {
+ const persistedState = persisted as {
+ lastSelectedId?: string;
+ };
+ const restored =
+ persistedState.lastSelectedId &&
+ persistedState.lastSelectedId in SKILL_BUTTONS
+ ? (persistedState.lastSelectedId as SkillButtonId)
+ : DEFAULT_PRIMARY;
+ return {
+ ...current,
+ lastSelectedId: restored,
+ };
+ },
+ },
+ ),
+);
diff --git a/apps/code/src/shared/types/analytics.ts b/apps/code/src/shared/types/analytics.ts
index 1c612c4aa..03740d8f8 100644
--- a/apps/code/src/shared/types/analytics.ts
+++ b/apps/code/src/shared/types/analytics.ts
@@ -19,6 +19,14 @@ export type FeedbackType = "good" | "bad" | "general";
type FileOpenSource = "sidebar" | "agent-suggestion" | "search" | "diff";
export type FileChangeType = "added" | "modified" | "deleted";
type StopReason = "user_cancelled" | "completed" | "error" | "timeout";
+export type SkillButtonId =
+ | "add-analytics"
+ | "create-feature-flags"
+ | "run-experiment"
+ | "add-error-tracking"
+ | "instrument-llm-calls"
+ | "add-logging";
+type SkillButtonSource = "primary" | "dropdown";
export type CommandMenuAction =
| "home"
| "new-task"
@@ -158,6 +166,12 @@ export interface CommandMenuActionProperties {
action_type: CommandMenuAction;
}
+export interface SkillButtonTriggeredProperties {
+ task_id: string;
+ button_id: SkillButtonId;
+ source: SkillButtonSource;
+}
+
// Settings events
export interface SettingChangedProperties {
setting_name: string;
@@ -280,6 +294,7 @@ export const ANALYTICS_EVENTS = {
COMMAND_MENU_OPENED: "Command menu opened",
COMMAND_MENU_ACTION: "Command menu action",
COMMAND_CENTER_VIEWED: "Command center viewed",
+ SKILL_BUTTON_TRIGGERED: "Skill button triggered",
// Permission events
PERMISSION_RESPONDED: "Permission responded",
@@ -343,6 +358,7 @@ export type EventPropertyMap = {
[ANALYTICS_EVENTS.COMMAND_MENU_OPENED]: never;
[ANALYTICS_EVENTS.COMMAND_MENU_ACTION]: CommandMenuActionProperties;
[ANALYTICS_EVENTS.COMMAND_CENTER_VIEWED]: never;
+ [ANALYTICS_EVENTS.SKILL_BUTTON_TRIGGERED]: SkillButtonTriggeredProperties;
// Permission events
[ANALYTICS_EVENTS.PERMISSION_RESPONDED]: PermissionRespondedProperties;