Token导航 LogoToken导航

Agent Skill · 研究检索

saas-product(SaaS 产品)

saas-product 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

saas-product 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 44,817。

总安装 890周安装 36GitHub Stars 2下载 279核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:saas-product(SaaS 产品)
来源仓库:https://github.com/tgautier/dotfiles
仓库路径:skills/saas-product
安装命令:
npx skills add https://github.com/tgautier/dotfiles --skill saas-product
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/tgautier/dotfiles --skill saas-product

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

saas-product 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中快速定位候选结果。适用于关键词搜索、任务场景匹配和来源线索筛选等研究检索场景。通过 npx skills add 命令从指定 GitHub 仓库安装并使用。

安装前需确认权限范围、维护状态及是否触发联网或文件操作。建议结合原始 README 和仓库内容进一步核验具体用法和功能边界。

来源仓库内路径:skills/saas-product

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/tgautier/dotfiles --skill saas-product;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

saas-product 怎么安装?

在终端执行 npx skills add https://github.com/tgautier/dotfiles --skill saas-product。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

saas-product 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

saas-product 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

saas-product 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

saas-product 用的人多吗?

公开数据是累计安装 890 次,近一周安装 36 次,下载 279 次,GitHub 2 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

saas-product 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
saas-product

SaaS 产品 · 本页

8902需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

SaaS Product Design

Methodology for building production SaaS features that drive user adoption and retention. Focused on patterns that reduce time-to-value and handle the complexity of multi-tenant, subscription-based applications.

For design system tokens and accessibility, see /ux-design. For React component patterns, see /react. For API contracts behind features, see /api-design. For domain modeling, see /domain-design.


1. Design Philosophy

Progressive complexity

Start simple, reveal complexity as the user needs it. A new user should reach their first moment of value within 60 seconds. Advanced features unlock progressively.

PrincipleApplication
Time-to-valueMinimize steps between signup and first meaningful action
Progressive disclosureHide advanced options behind "Advanced" toggles or secondary menus
Jobs-to-be-doneDesign around what the user is trying to accomplish, not around data entities
Sensible defaultsPre-fill settings with the most common choices; make the default path correct
Undo over confirmPrefer reversible actions with undo over confirmation dialogs that interrupt flow

Product hierarchy

Feature → Page → Section → Component

Each level has a clear responsibility:

  • Feature: A complete capability (e.g., "Asset Tracking")
  • Page: A view within a feature (e.g., "Asset List", "Asset Detail")
  • Section: A logical grouping within a page (e.g., "Performance Chart", "Transaction History")
  • Component: A reusable UI element (e.g., "Currency Badge", "Date Picker")

2. Onboarding & First-Run

Activation metrics

Define what "activated" means before building onboarding:

MetricExample
Setup completeUser has connected at least one data source
First valueUser has viewed their first dashboard with real data
Habit formedUser returns 3 times in the first 7 days

Progressive onboarding patterns

Setup wizard — for products requiring initial configuration:

// Loader returns current step from session/DB
export async function loader({ request }: LoaderFunctionArgs) {
  const progress = await getOnboardingProgress(request);
  return { step: progress.currentStep, steps: progress.steps };
}

// Action validates current step, saves, and advances
export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  if (formData.get("_intent") === "skip") return redirect("/app");
  const nextStep = await saveStepAndAdvance(formData);
  if (nextStep === "complete") return redirect("/app");
  return redirect(`/onboarding/step/${nextStep}`);
}

// Component renders the current step as a form
function SetupWizard() {
  const { step, steps } = useLoaderData<typeof loader>();
  return (
    <div>
      <StepIndicator steps={steps} current={step} />
      <Form method="post">
        <CurrentStepFields step={step} />
        <Button type="submit">Continue</Button>
      </Form>
      <Form method="post">
        <input type="hidden" name="_intent" value="skip" />
        <button type="submit" className="text-sm text-muted-foreground">
          Skip setup — I'll do this later
        </button>
      </Form>
    </div>
  );
}

Rules:

  • Always allow skipping — never force completion of all steps
  • Progress is saved automatically — each step is an action submission persisted server-side
  • Max 3-5 steps — more than that and users abandon
  • Show progress clearly (step 2 of 4)

Checklist pattern — for products where setup is gradual:

function OnboardingChecklist({ tasks }: { tasks: OnboardingTask[] }) {
  const completed = tasks.filter(t => t.done).length;
  return (
    <Card>
      <Progress value={completed} max={tasks.length} />
      <p>{completed} of {tasks.length} complete</p>
      {tasks.map(task => (
        <ChecklistItem key={task.id} task={task} />
      ))}
      {completed === tasks.length && <DismissButton />}
    </Card>
  );
}

Contextual tooltips — for feature discovery after initial setup:

  • Show once per user, track dismissal in user preferences
  • Point to the specific UI element, not a general area
  • Include a single clear CTA ("Try it now" / "Got it")

Anti-patterns

  • Forced video tours (users skip them)
  • Tooltips on every element simultaneously (overwhelming)
  • Blocking the app until onboarding is complete (drives abandonment)
  • Showing onboarding to returning users who already completed it

3. Empty States

Every data-driven view must handle four empty conditions:

TypeWhenContent
First-useUser hasn't created any data yetIllustration + explanation + primary CTA
No resultsSearch or filter returned nothing"No results for X" + suggestion to broaden search
ErrorData failed to loadError message + retry button
Filtered emptyApplied filters exclude all resultsShow active filters + "Clear filters" button

First-use empty state pattern

function EmptyState({ icon, title, description, action }: EmptyStateProps) {
  return (
    <div className="flex flex-col items-center justify-center py-16 text-center">
      <div className="mb-4 text-muted-foreground">{icon}</div>
      <h3 className="text-lg font-semibold">{title}</h3>
      <p className="mt-1 max-w-sm text-sm text-muted-foreground">{description}</p>
      {action && (
        <Button className="mt-4" asChild>
          <Link to={action.href}>{action.label}</Link>
        </Button>
      )}
    </div>
  );
}

// Usage — CTA navigates to a route, not an onClick handler
<EmptyState
  icon={<WalletIcon size={48} />}
  title="No assets yet"
  description="Add your first asset to start tracking your portfolio performance."
  action={{ href: "/assets/new", label: "Add Asset" }}
/>

Rules:

  • First-use empty states must have a CTA that leads to creating the first item
  • Never show a blank page or a lonely "No data" message
  • Use illustrations or icons to make the empty state feel intentional, not broken
  • Reduce the CTA to a single clear action — don't offer multiple paths

4. Dashboard Design

KPI card anatomy

A well-designed KPI card shows: current value, trend indicator, comparison period, and optional sparkline.

interface KPICardProps {
  label: string;
  value: string;
  change: number;      // percentage change
  period: string;      // "vs last month"
  sparklineData?: number[];
}

function KPICard({ label, value, change, period, sparklineData }: KPICardProps) {
  const isPositive = change >= 0;
  return (
    <Card>
      <p className="text-sm text-muted-foreground">{label}</p>
      <p className="text-2xl font-bold">{value}</p>
      <div className="flex items-center gap-1 text-sm">
        <TrendIcon direction={isPositive ? "up" : "down"} />
        <span className={isPositive ? "text-green-600" : "text-red-600"}>
          {Math.abs(change)}%
        </span>
        <span className="text-muted-foreground">{period}</span>
      </div>
      {sparklineData && <Sparkline data={sparklineData} />}
    </Card>
  );
}

Chart selection guide

Data relationshipChart typeWhen to use
Part-to-wholeDonut (max 5 segments)Budget allocation, portfolio mix
Change over timeLine / areaRevenue trends, growth metrics
ComparisonHorizontal barCategory comparison, rankings
DistributionHistogramValue ranges, frequency
Composition over timeStacked areaRevenue by segment over time

Rules:

  • Never use pie charts for more than 5 segments — switch to horizontal bar
  • Never use 3D charts
  • Line charts require a continuous x-axis (time, sequence)
  • Always label axes and include units

Dashboard layout

  • Top row: 3-4 KPI cards summarizing the most important metrics
  • Middle: Primary chart (full width or 2/3 width)
  • Bottom: Secondary data tables or detail views
  • Use CSS Grid for the layout: grid-cols-1 md:grid-cols-2 lg:grid-cols-4 for KPI row

Data density

  • Dense displays for power users (tables with many columns, compact spacing)
  • Summary views for casual users (KPI cards, sparklines, simplified charts)
  • Let users toggle between views or remember their preference

5. Loading & Transition States

Skeleton screens

Match the skeleton shape to the actual content layout. Users perceive skeleton screens as faster than spinners:

function AssetListSkeleton() {
  return (
    <div className="space-y-3">
      {Array.from({ length: 5 }).map((_, i) => (
        <div key={i} className="flex items-center gap-4">
          <Skeleton className="h-10 w-10 rounded-full" />
          <div className="space-y-2">
            <Skeleton className="h-4 w-48" />
            <Skeleton className="h-3 w-32" />
          </div>
          <Skeleton className="ml-auto h-4 w-20" />
        </div>
      ))}
    </div>
  );
}

Loading state decision framework

DurationPattern
< 100msNo indicator needed
100-300msSubtle inline indicator (button spinner)
300ms-2sSkeleton screen
2-10sProgress bar or skeleton with message
> 10sBackground task with notification on completion

Optimistic UI

For mutations where failure is rare, show the expected result immediately. In React Router v7, derive optimistic state from fetcher.formData — the pending submission data. Render pending items separately from the data list — the optimistic item is transient UI state, not data. The server assigns the real ID via loader revalidation:

function TodoList({ items }: { items: Todo[] }) {
  const fetcher = useFetcher();

  return (
    <>
      <ul>
        {items.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
        {fetcher.formData && (
          <li className="opacity-50">
            {String(fetcher.formData.get("name") ?? "")}
          </li>
        )}
      </ul>
      <fetcher.Form method="post">
        <input type="hidden" name="_intent" value="create" />
        <input name="name" required />
        <Button type="submit">Add</Button>
      </fetcher.Form>
    </>
  );
}
  • fetcher.formData is non-null while the submission is in flight — derive the optimistic item directly from it
  • When the action completes, loaders revalidate, items updates with the real data, and fetcher.formData resets to null
  • On failure, loaders still revalidate with unchanged data — the optimistic item disappears because fetcher.formData is null
  • No useOptimistic, no onSubmit, no startTransition — React Router's data layer handles the lifecycle

Streaming SSR

For pages with mixed fast/slow data sources:

  • Fast data (navigation, layout) renders immediately in the shell
  • Slow data (analytics, external APIs) streams in via Suspense boundaries
  • Each Suspense boundary shows its own skeleton while loading

Server-first loading principle

With route loaders, data is available when the page renders — no loading spinners for initial data. Reserve skeleton screens for:

  • Streaming SSR (slow data sources behind Suspense boundaries)
  • Fetcher-driven updates (non-navigation mutations in progress)
  • Client-only components that need a mount guard

6. Notification Design

Notification channels

ChannelUse forUrgency
In-app toastAction confirmation, minor errorsLow — auto-dismiss 5s
In-app bellNew activity, status changesMedium — persists until read
EmailTransactional (receipts, invites), digestsLow — batched where possible
Browser pushTime-sensitive alerts onlyHigh — interrupts

Toast best practices

// Success toast with undo
showToast("Asset deleted", {
  action: { label: "Undo", onClick: undoDelete },
  duration: 5000,
});

// Error toast — persists until dismissed
showToast("Failed to save changes. Please try again.", {
  variant: "error",
  duration: Infinity,
  action: { label: "Retry", onClick: retryAction },
});

Rules:

  • Auto-dismiss success toasts after 5 seconds
  • Never auto-dismiss error toasts — the user may not notice them
  • Provide an undo action for destructive operations
  • Stack multiple toasts vertically, limit to 3 visible simultaneously
  • Never use toasts as the sole error indicator for form validation

Notification preferences

Implement as a channel-by-event matrix:

EventIn-AppEmailPush
New team memberDefault onDefault onDefault off
Weekly digestN/ADefault onN/A
Payment failedDefault onDefault onDefault on
Feature updateDefault onDefault offDefault off

Let users control each cell independently.


7. Billing & Subscription UX

Plan comparison

function PlanComparison({ plans }: { plans: Plan[] }) {
  return (
    <div className="grid gap-6 md:grid-cols-3">
      {plans.map(plan => (
        <PlanCard
          key={plan.id}
          name={plan.name}
          price={plan.price}
          period={plan.period}
          features={plan.features}
          recommended={plan.recommended}
          current={plan.current}
        />
      ))}
    </div>
  );
}

Rules:

  • Highlight the recommended plan visually (border, badge, "Most Popular")
  • Show the current plan clearly so the user knows where they are
  • List features as checkmarks per plan — show what's included AND what's not
  • Annual pricing should show the monthly equivalent and savings percentage

Upgrade prompts

Contextual prompts are 3x more effective than generic upsell banners:

// GOOD — contextual, shown when the user hits a limit
function FeatureLimitPrompt({ feature, limit, current }: LimitPromptProps) {
  return (
    <Alert>
      <p>You've used {current} of {limit} {feature}.</p>
      <Button variant="link" asChild>
        <Link to="/settings/billing">Upgrade for unlimited {feature}</Link>
      </Button>
    </Alert>
  );
}

// BAD — generic banner shown on every page
<Banner>Upgrade to Pro for more features!</Banner>

Trial and downgrade

  • Show trial days remaining in a subtle, persistent indicator (not a popup)
  • Before downgrade: show what the user will lose, not just the features list
  • After downgrade: gracefully degrade features (read-only, not deleted)
  • Never delete user data on downgrade — mark it as inaccessible and allow re-upgrade

8. Feature Gating

Implementation patterns

PatternUse when
Feature flagRolling out new features gradually (% of users)
Plan gatingFeature is available only on certain subscription tiers
Role gatingFeature is restricted to certain user roles (admin, member)
Usage limitFeature has a quota per billing period

Graceful degradation

When a feature is gated, show the user what they're missing and how to get it:

// Check access in the loader — never send gated data to the client
export async function loader({ request }: LoaderFunctionArgs) {
  const user = await requireAuth(request);
  const access = await checkFeatureAccess(user, "advanced-analytics");
  if (!access.granted) {
    return { gated: true, requiredPlan: access.requiredPlan };
  }
  const data = await loadAnalytics();
  return { gated: false, data };
}

// Component renders based on loader data
function AnalyticsPage() {
  const loaderData = useLoaderData<typeof loader>();
  if (loaderData.gated) {
    return <UpgradeOverlay requiredPlan={loaderData.requiredPlan} />;
  }
  return <AnalyticsDashboard data={loaderData.data} />;
}

Rules:

  • Never show a blank space where a gated feature should be — show a teaser
  • Don't hide gated features entirely — discovery drives upgrades
  • Use blurred previews or locked icons, not error messages
  • Role-gated features should show a "Contact your admin" message, not an upgrade prompt

9. Settings & Admin UX

Settings organization

SectionContents
AccountProfile, email, password, 2FA
TeamMembers, invitations, roles
BillingPlan, payment method, invoices
PreferencesTheme, language, notification settings
IntegrationsConnected services, API keys
Danger zoneDelete account, export data

Danger zone

Destructive settings must be visually distinct and require confirmation:

// Action — server-side validation (client-side pattern is bypassable)
export async function action({ request }: ActionFunctionArgs) {
  const user = await requireAuth(request);
  const formData = await request.formData();
  if (formData.get("_intent") === "delete-account") {
    const confirmation = String(formData.get("confirmation") ?? "");
    if (confirmation !== "delete my account") {
      return Response.json(
        { error: "Confirmation phrase does not match", intent: "delete-account" },
        { status: 400 },
      );
    }
    await api.deleteAccount(user.id);
    return redirect("/goodbye");
  }
}

// Component
function DangerZone() {
  return (
    <Card className="border-red-200 bg-red-50">
      <h3 className="text-red-900">Danger Zone</h3>
      <div className="space-y-4">
        <Form method="post">
          <input type="hidden" name="_intent" value="delete-account" />
          <p className="text-sm">Permanently delete your account and all data. This cannot be undone.</p>
          <label htmlFor="delete-confirmation" className="text-sm font-medium">
            Type "delete my account" to confirm
          </label>
          <input
            id="delete-confirmation"
            name="confirmation"
            required
            pattern="delete my account"
          />
          <Button type="submit" variant="destructive">Delete account</Button>
        </Form>
      </div>
    </Card>
  );
}

Rules:

  • Red border/background for the danger zone section
  • Require typing a confirmation phrase for irreversible actions
  • Server action validates the confirmation value — client-side pattern is a UX hint, not a security boundary
  • Show a clear description of what will be deleted/lost
  • Offer data export before account deletion

10. Audit Trails & Activity Feeds

Feed structure

Every audit entry answers: who did what to which resource and when.

interface AuditEntry {
  id: string;
  actor: { id: string; name: string; avatar?: string };
  action: string;         // "created" | "updated" | "deleted" | "exported"
  resource: { type: string; id: string; name: string };
  changes?: FieldChange[];
  timestamp: string;      // ISO 8601
}

interface FieldChange {
  field: string;
  from: string | null;
  to: string | null;
}

Display patterns

  • Group entries by day with date headers
  • Show the most recent activity first
  • Paginate with "Load more" (not page numbers) for chronological feeds
  • Filter by: actor, action type, resource type, date range
  • For field changes, show a diff view: old value → new value

11. Multi-Tenancy Awareness

Tenant context in UI

  • Always show the current organization/workspace name in the sidebar or header
  • Org switcher should be prominent and always accessible
  • After switching orgs, redirect to the new org's dashboard (not the same page, which may not exist)

Data isolation

  • Every API request must include tenant context (header, path param, or session)
  • Never show data from other tenants — even in error messages
  • Search results must be scoped to the current tenant
  • URL paths should include the tenant identifier for shareable links: /org/{org-id}/assets

Shared resources

Some resources span tenants (billing admin, super admin views). Clearly distinguish:

  • Tenant-scoped views: normal styling
  • Cross-tenant views: distinct visual treatment (different background, admin badge)

12. Anti-Patterns

Anti-patternWhy it failsBetter approach
Blocking modal on first visitUsers close it immediately, miss the contentInline checklist or contextual hints
"No data" as empty stateFeels broken, gives no guidanceFirst-use empty state with CTA
Spinner for every loadUsers perceive it as slowSkeleton screens matching content shape
Generic upgrade bannerBanner blindness, users ignore itContextual prompts when hitting limits
Settings as a flat listOverwhelming, hard to find thingsGrouped sections with clear hierarchy
Hiding features behind menusLow discoverabilityProgressive disclosure with visual cues
Confirmation dialog for every actionDialog fatigue, users click without readingUndo pattern for reversible actions
Email-only notificationsUsers miss them, no in-app awarenessIn-app notification center + email fallback
All-or-nothing free planHigh barrier to conversionGenerous free tier with usage-based limits
Instant data deletion on downgradeUsers fear committing to plansGrace period + read-only access
Activity feed without filtersNoise drowns signal for active orgsFilter by actor, action, resource, date
No tenant indicator in UIUsers accidentally modify wrong orgAlways show current org + easy switching
SPA-era: client-side wizard stateProgress lost on refresh, no deep links, no back buttonServer-managed steps via loader/action
SPA-era: onClick handlers for mutationsNo progressive enhancement, no revalidation<Form method="post"> with intent pattern
SPA-era: client-side feature gatingGated data still sent to client, security riskCheck access in loader, never send gated data

Cross-references

  • /ux-design — design tokens, accessibility, component API design, form UX
  • /react — React component patterns, hooks, state management
  • /api-design — REST contracts, pagination, error formats behind features
  • /domain-design — aggregate boundaries, entity vs value object, domain events

平台分布

Codex

34.92%
按下载量换算97

Claude

31.93%
按下载量换算89

Cursor

20.76%
按下载量换算58

Gemini CLI

9.1%
按下载量换算25

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills