skills/tools-posthog/SKILL.md
Standard pattern for implementing freemium/pro features with PostHog feature flags, usage tracking, upgrade prompts, and Stripe-ready payment hooks. Use when adding any feature that will eventually be paid.
npx skillsauth add aussiegingersnap/cursor-skills tools-posthogInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Pattern for implementing freemium/pro features with per-user feature flags, usage measurement, and payment-ready architecture.
┌─────────────────────────────────────────────────────────┐
│ PostHog │
│ ┌─────────────────────────────────────────────────────┐│
│ │ Feature Flag: cloud-notes-sync ││
│ │ Targeting: plan = 'pro' OR beta-tester = true ││
│ └─────────────────────────────────────────────────────┘│
└──────────────────────┬──────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────┐
│ Client │
│ ┌─────────────────┐ ┌────────────────────────────────┐│
│ │ useProFeature() │──│ Feature Component ││
│ │ - enabled │ │ - Show feature if enabled ││
│ │ - trackAttempt │ │ - Show upgrade prompt if not ││
│ └─────────────────┘ └────────────────────────────────┘│
└──────────────────────────────────────────────────────────┘
Use descriptive, kebab-case names:
{feature-name}-enabled
Examples:
- cloud-notes-sync
- advanced-models
- export-features
- unlimited-messages
cloud-notes-syncbeta-tester is trueplan is proSet these properties when user state changes:
// When user subscribes
posthog.setPersonProperties({
plan: 'pro',
subscribed_at: new Date().toISOString(),
})
// When user joins beta
posthog.setPersonProperties({
'beta-tester': true,
})
// hooks/useFeatureFlags.ts
import { useEffect, useState, useCallback } from 'react'
import { posthog } from '@/lib/posthog'
export function useFeatureFlag(flagKey: string): boolean {
const [enabled, setEnabled] = useState(false)
useEffect(() => {
if (typeof window === 'undefined') return
const checkFlag = () => {
const value = posthog.isFeatureEnabled(flagKey)
setEnabled(value ?? false)
}
if (posthog.__loaded) checkFlag()
posthog.onFeatureFlags(checkFlag)
}, [flagKey])
return enabled
}
export function useProFeature(flagKey: string): {
enabled: boolean
loading: boolean
trackAttempt: () => void
trackUpgradeShown: () => void
trackUpgradeClicked: () => void
} {
const [enabled, setEnabled] = useState(false)
const [loading, setLoading] = useState(true)
useEffect(() => {
const checkFlag = () => {
setEnabled(posthog.isFeatureEnabled(flagKey) ?? false)
setLoading(false)
}
if (posthog.__loaded) checkFlag()
posthog.onFeatureFlags(checkFlag)
}, [flagKey])
const trackAttempt = useCallback(() => {
posthog.capture(`${flagKey}_attempted`)
}, [flagKey])
const trackUpgradeShown = useCallback(() => {
posthog.capture(`${flagKey}_upgrade_shown`)
}, [flagKey])
const trackUpgradeClicked = useCallback(() => {
posthog.capture(`${flagKey}_upgrade_clicked`)
}, [flagKey])
return { enabled, loading, trackAttempt, trackUpgradeShown, trackUpgradeClicked }
}
Create specific hooks for each feature:
export function useCloudNotesSync() {
return useProFeature('cloud-notes-sync')
}
export function useAdvancedModels() {
return useProFeature('advanced-models')
}
Track feature usage to measure demand and conversion:
| Event | When | Properties |
|-------|------|------------|
| {feature}_attempted | User tries to use gated feature | { feature } |
| {feature}_completed | Feature used successfully | { feature, duration_ms? } |
| {feature}_upgrade_shown | Upgrade prompt displayed | { feature, trigger } |
| {feature}_upgrade_clicked | User clicked upgrade CTA | { feature } |
// Track when user tries to use feature
const handleFeatureClick = () => {
if (!cloudSync.enabled) {
cloudSync.trackAttempt()
cloudSync.trackUpgradeShown()
setShowUpgradeModal(true)
return
}
// ... feature logic
}
function UpgradeModal({ feature, onClose }: { feature: string; onClose: () => void }) {
const { trackUpgradeClicked } = useProFeature(feature)
return (
<Dialog open onOpenChange={onClose}>
<DialogContent>
<DialogHeader>
<DialogTitle>Upgrade to Pro</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<p>Get access to cloud sync and more for just $7/month.</p>
<ul className="space-y-2">
<li>✓ Sync notes across devices</li>
<li>✓ Cloud backup</li>
<li>✓ Export options</li>
</ul>
<Button
onClick={() => {
trackUpgradeClicked()
window.location.href = '/pricing'
}}
>
Upgrade Now — $7/mo
</Button>
</div>
</DialogContent>
</Dialog>
)
}
function FeatureLock({
feature,
children
}: {
feature: string
children: React.ReactNode
}) {
const { enabled, loading, trackAttempt, trackUpgradeShown } = useProFeature(feature)
if (loading) return <Skeleton />
if (!enabled) {
useEffect(() => {
trackAttempt()
trackUpgradeShown()
}, [])
return (
<div className="relative">
<div className="opacity-50 pointer-events-none blur-sm">
{children}
</div>
<div className="absolute inset-0 flex items-center justify-center">
<ProBadge />
<span>Pro Feature</span>
</div>
</div>
)
}
return <>{children}</>
}
Structure code so payment integration is additive:
// User properties are set manually or via feature flags
posthog.setPersonProperties({ plan: 'free' })
// After Stripe webhook confirms subscription
const handleSubscriptionCreated = async (subscription: Stripe.Subscription) => {
const plan = subscription.items.data[0].price.lookup_key // 'pro' or 'free'
// Update PostHog for feature flag targeting
posthog.setPersonProperties({
plan,
stripe_customer_id: subscription.customer,
subscribed_at: new Date().toISOString(),
})
// PostHog flag `cloud-notes-sync` targets `plan: 'pro'`
// Features automatically unlock
}
// app/pricing/page.tsx
export default function PricingPage() {
return (
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
<PricingCard
plan="Free"
price="$0"
features={[
'Local notes storage',
'50 AI messages/day',
'Basic Bible reading',
]}
cta="Current Plan"
disabled
/>
<PricingCard
plan="Pro"
price="$7"
period="/month"
features={[
'Cloud sync across devices',
'Unlimited AI messages',
'Advanced AI models',
'Export notes & highlights',
'Priority support',
]}
cta="Upgrade to Pro"
highlighted
onClick={() => {/* Stripe checkout */}}
/>
</div>
)
}
When adding a new pro feature:
useFeatureFlags.tsenabled check!enabled_attempted when user tries to use_upgrade_shown when prompt displays_upgrade_clicked when user clicks CTA_completed when feature is used successfully| Item | Pattern |
|------|---------|
| Flag naming | {feature-name} (kebab-case) |
| Hook | useProFeature('flag-key') |
| Tracking | trackAttempt(), trackUpgradeShown(), trackUpgradeClicked() |
| Targeting | Set plan: 'pro' in PostHog properties |
| Typical price | $5-15/month for indie SaaS |
| Feature | Free | Pro | |---------|------|-----| | Cloud sync | Local storage only | Cross-device sync | | API usage | 25 calls/day | Unlimited | | AI models | GPT-4o-mini | Claude, GPT-4o, etc. | | Export | None | PDF, Markdown | | Support | Community | Priority |
tools
# Versioning Skill Semantic versioning automation based on conventional commits. Automatically manages version bumps, changelogs, and git tags using `standard-version`. ## When to Use - Before releasing a new version - When preparing a deployment - To generate/update CHANGELOG.md - When the user asks about version management - Setting up versioning for a new project ## Prerequisites - Conventional commits enforced (recommended: lefthook) - Node.js project with package.json ## Setup (One-Ti
tools
Theme generation with tweakcn for shadcn/ui and Magic UI animations. Use when setting up project themes, customizing color schemes, adding dark mode, or integrating animated components.
tools
shadcn/studio component library with MCP integration, theme generation, and block patterns. This skill should be used when building UI with shadcn components, selecting dashboard layouts, or generating landing pages. Canonical source for all shadcn-based work.
development
Enforce a precise, minimal design system inspired by Linear, Notion, and Stripe. Use this skill when building dashboards, admin interfaces, or any UI that needs Jony Ive-level precision - clean, modern, minimalist with taste. Every pixel matters.