From d6ec115348d0581fc2e6729298db7f31c776d1d6 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Tue, 7 Apr 2026 16:11:31 -0700 Subject: [PATCH 1/2] v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha --- apps/sim/app/(auth)/signup/signup-form.tsx | 11 +++-------- .../app/workspace/[workspaceId]/home/home.tsx | 12 ++++++++++-- .../w/[workflowId]/components/panel/panel.tsx | 19 ++++++++++++++++++- apps/sim/lib/posthog/events.ts | 5 +++++ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/sim/app/(auth)/signup/signup-form.tsx b/apps/sim/app/(auth)/signup/signup-form.tsx index 55a0508ec1b..afb27cd729a 100644 --- a/apps/sim/app/(auth)/signup/signup-form.tsx +++ b/apps/sim/app/(auth)/signup/signup-form.tsx @@ -270,10 +270,8 @@ function SignupFormContent({ name: sanitizedName, }, { - fetchOptions: { - headers: { - ...(token ? { 'x-captcha-response': token } : {}), - }, + headers: { + ...(token ? { 'x-captcha-response': token } : {}), }, onError: (ctx) => { logger.error('Signup error:', ctx.error) @@ -282,10 +280,7 @@ function SignupFormContent({ let errorCode = 'unknown' if (ctx.error.code?.includes('USER_ALREADY_EXISTS')) { errorCode = 'user_already_exists' - errorMessage.push( - 'An account with this email already exists. Please sign in instead.' - ) - setEmailError(errorMessage[0]) + setEmailError('An account with this email already exists. Please sign in instead.') } else if ( ctx.error.code?.includes('BAD_REQUEST') || ctx.error.message?.includes('Email and password sign up is not enabled') diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index d76f17ff454..38367339197 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -223,6 +223,14 @@ export function Home({ chatId }: HomeProps = {}) { posthogRef.current = posthog }, [posthog]) + const handleStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'mothership', + }) + stopGeneration() + }, [stopGeneration, workspaceId]) + const handleSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() @@ -334,7 +342,7 @@ export function Home({ chatId }: HomeProps = {}) { defaultValue={initialPrompt} onSubmit={handleSubmit} isSending={isSending} - onStopGeneration={stopGeneration} + onStopGeneration={handleStopGeneration} userId={session?.user?.id} onContextAdd={handleContextAdd} /> @@ -359,7 +367,7 @@ export function Home({ chatId }: HomeProps = {}) { isSending={isSending} isReconnecting={isReconnecting} onSubmit={handleSubmit} - onStopGeneration={stopGeneration} + onStopGeneration={handleStopGeneration} messageQueue={messageQueue} onRemoveQueuedMessage={removeFromQueue} onSendQueuedMessage={sendNow} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 4d485c763ce..da51910789b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -4,6 +4,7 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' import { History, Plus, Square } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' +import { usePostHog } from 'posthog-js/react' import { useShallow } from 'zustand/react/shallow' import { BubbleChatClose, @@ -33,6 +34,7 @@ import { import { Lock, Unlock, Upload } from '@/components/emcn/icons' import { VariableIcon } from '@/components/icons' import { useSession } from '@/lib/auth/auth-client' +import { captureEvent } from '@/lib/posthog/client' import { generateWorkflowJson } from '@/lib/workflows/operations/import-export' import { ConversationListItem } from '@/app/workspace/[workspaceId]/components' import { MothershipChat } from '@/app/workspace/[workspaceId]/home/components' @@ -101,6 +103,9 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel const params = useParams() const workspaceId = propWorkspaceId ?? (params.workspaceId as string) + const posthog = usePostHog() + const posthogRef = useRef(posthog) + const panelRef = useRef(null) const fileInputRef = useRef(null) const { activeTab, setActiveTab, panelWidth, _hasHydrated, setHasHydrated } = usePanelStore( @@ -264,6 +269,10 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel loadCopilotChats() }, [loadCopilotChats]) + useEffect(() => { + posthogRef.current = posthog + }, [posthog]) + const handleCopilotSelectChat = useCallback((chat: { id: string; title: string | null }) => { setCopilotChatId(chat.id) setCopilotChatTitle(chat.title) @@ -394,6 +403,14 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel [copilotEditQueuedMessage] ) + const handleCopilotStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'copilot', + }) + copilotStopGeneration() + }, [copilotStopGeneration, workspaceId]) + const handleCopilotSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() @@ -833,7 +850,7 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel isSending={copilotIsSending} isReconnecting={copilotIsReconnecting} onSubmit={handleCopilotSubmit} - onStopGeneration={copilotStopGeneration} + onStopGeneration={handleCopilotStopGeneration} messageQueue={copilotMessageQueue} onRemoveQueuedMessage={copilotRemoveFromQueue} onSendQueuedMessage={copilotSendNow} diff --git a/apps/sim/lib/posthog/events.ts b/apps/sim/lib/posthog/events.ts index 537a9864282..faf9895bf62 100644 --- a/apps/sim/lib/posthog/events.ts +++ b/apps/sim/lib/posthog/events.ts @@ -378,6 +378,11 @@ export interface PostHogEventMap { workspace_id: string } + task_generation_aborted: { + workspace_id: string + view: 'mothership' | 'copilot' + } + task_message_sent: { workspace_id: string has_attachments: boolean From 65e271421746606d66001692e5e0ddb2f09ca49b Mon Sep 17 00:00:00 2001 From: thejesh23 Date: Sun, 12 Jul 2026 03:22:08 -0700 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20quote=20argument-hint=20YAML=20value?= =?UTF-8?q?s=20so=20Copilot=20CLI=20=E2=89=A51.0.65=20loads=20all=20skills?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `argument-hint: [foo]` YAML-parses as a flow sequence (array), not a string. Downstream slash-command loaders that validate `argument-hint` as a string — notably GitHub Copilot CLI ≥ 1.0.65 — silently reject the skill on load, and the command disappears from the CLI menu. Wrap the value in double quotes so it parses as a string. No behaviour change on Claude Code. --- .claude/commands/cleanup.md | 2 +- .claude/commands/emcn-design-review.md | 2 +- .claude/commands/react-query-best-practices.md | 2 +- .claude/commands/ship.md | 2 +- .claude/commands/you-might-not-need-a-callback.md | 2 +- .claude/commands/you-might-not-need-a-memo.md | 2 +- .claude/commands/you-might-not-need-an-effect.md | 2 +- .claude/commands/you-might-not-need-state.md | 2 +- .claude/commands/you-might-not-need-url-state.md | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.claude/commands/cleanup.md b/.claude/commands/cleanup.md index 34bce559f4b..b0cbbb42fae 100644 --- a/.claude/commands/cleanup.md +++ b/.claude/commands/cleanup.md @@ -1,6 +1,6 @@ --- description: Run all code quality skills in sequence — effects, memo, callbacks, state, React Query, emcn design review, and url-state -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # Cleanup diff --git a/.claude/commands/emcn-design-review.md b/.claude/commands/emcn-design-review.md index 0c96b074796..741c02c64b9 100644 --- a/.claude/commands/emcn-design-review.md +++ b/.claude/commands/emcn-design-review.md @@ -1,6 +1,6 @@ --- description: Review UI code for alignment with the emcn design system — components, tokens, patterns, and conventions -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # EMCN Design Review diff --git a/.claude/commands/react-query-best-practices.md b/.claude/commands/react-query-best-practices.md index 0fc450958bf..a1222866f96 100644 --- a/.claude/commands/react-query-best-practices.md +++ b/.claude/commands/react-query-best-practices.md @@ -1,6 +1,6 @@ --- description: Audit React Query usage for best practices — key factories, staleTime, mutations, and server state ownership -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # React Query Best Practices diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md index 6141fa01c1a..df54f36b95d 100644 --- a/.claude/commands/ship.md +++ b/.claude/commands/ship.md @@ -1,6 +1,6 @@ --- description: Commit, push, and open a PR to staging in one shot -argument-hint: [optional context or scope notes] +argument-hint: "[optional context or scope notes]" --- # Ship Command diff --git a/.claude/commands/you-might-not-need-a-callback.md b/.claude/commands/you-might-not-need-a-callback.md index c25d4c32eab..a67f1ecb7da 100644 --- a/.claude/commands/you-might-not-need-a-callback.md +++ b/.claude/commands/you-might-not-need-a-callback.md @@ -1,6 +1,6 @@ --- description: Analyze and fix useCallback anti-patterns in your code -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # You Might Not Need a Callback diff --git a/.claude/commands/you-might-not-need-a-memo.md b/.claude/commands/you-might-not-need-a-memo.md index f0c5b3124ab..5b0f5b7983c 100644 --- a/.claude/commands/you-might-not-need-a-memo.md +++ b/.claude/commands/you-might-not-need-a-memo.md @@ -1,6 +1,6 @@ --- description: Analyze and fix useMemo/React.memo anti-patterns in your code -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # You Might Not Need a Memo diff --git a/.claude/commands/you-might-not-need-an-effect.md b/.claude/commands/you-might-not-need-an-effect.md index 1e798c804e9..cd438e6f01d 100644 --- a/.claude/commands/you-might-not-need-an-effect.md +++ b/.claude/commands/you-might-not-need-an-effect.md @@ -1,6 +1,6 @@ --- description: Analyze and fix useEffect anti-patterns in your code -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # You Might Not Need an Effect diff --git a/.claude/commands/you-might-not-need-state.md b/.claude/commands/you-might-not-need-state.md index 2e539146f6b..eab91ca7b37 100644 --- a/.claude/commands/you-might-not-need-state.md +++ b/.claude/commands/you-might-not-need-state.md @@ -1,6 +1,6 @@ --- description: Analyze and fix unnecessary useState, derived state, and server-state-in-local-state anti-patterns -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # You Might Not Need State diff --git a/.claude/commands/you-might-not-need-url-state.md b/.claude/commands/you-might-not-need-url-state.md index 77ba1ebeccb..bcab8ac2db6 100644 --- a/.claude/commands/you-might-not-need-url-state.md +++ b/.claude/commands/you-might-not-need-url-state.md @@ -1,6 +1,6 @@ --- description: Analyze and fix URL/query-param state anti-patterns — manual useSearchParams reads, hand-built query mutations, view-state trapped in useState, and objects in the URL -argument-hint: [scope] [fix=true|false] +argument-hint: "[scope] [fix=true|false]" --- # You Might Not Need URL State