All skills
vercel-labs avatar

/flags-sdk

@ce31fdf official

Set up and use feature flags and A/B tests with the Flags SDK (`flags` npm package) and Vercel Flags. Use when installing or configuring the SDK, adding a new or existing flag, wiring `vercelAdapter` (OIDC or SDK keys), declaring flags with `flag()`, using the `vercel flags` CLI (create, inspect, list, enable, disable, set, update, split, rollout, rules, segments, use-targeting, evaluations, versions, open, archive, unarchive, rm, sdk-keys, override, prepare), setting up providers/adapters (Vercel, Statsig, LaunchDarkly, PostHog, GrowthBook, Global Config, OpenFeature, Split, Flagsmith, Reflag, Optimizely, or custom), precompute, `identify`/`dedupe`, Flags Explorer/Toolbar, Next.js or SvelteKit, or encrypting flag values. Triggers: feature flags, feature gates, A/B testing, experimentation, gradual rollout, traffic split, targeting rules, flag overrides, precompute, Flags Explorer, Vercel Flags, vercel flags CLI, `flags/next`, `flags/sveltekit`, `flags/react`, `@flags-sdk/*`.

  • 11 files
  • 168.6 KB
  • Updated 13 hours ago
  • GitHub

Use this Skill: https://skilld.dev/gh/vercel-labs/vercel-plugin/flags-sdk

This session only. Nothing lands on disk.

referencesproviders.md

≈6.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Provider Adapters

Table of Contents


Vercel

Package: @flags-sdk/vercel

pnpm i flags @flags-sdk/vercel

Setup

Prerequisite: The Vercel CLI must be installed and authenticated. If it is not installed, run pnpm i -g vercel. For authentication or linking issues, follow the vercel-cli skill if it's already available. If it isn't, suggest the user install it with npx skills add vercel/vercel@vercel-cli (don't auto-install it) and rely on vercel <command> --help in the meantime.

The following steps integrate a flag into an app and need a linked project for vercel env pull; verify the link as described in Project targeting. To only inspect or change remote flags, follow CLI-only flag management instead.

  1. Create a flag in the Vercel dashboard or via CLI: vercel flags create <flag-key> --kind boolean --description "<description>"
  2. Pull env vars: run vercel env pull to write the Vercel OIDC token and the Development FLAGS_SECRET to .env.local (Pull environment variables). See Authentication for SDK keys.
  3. Declare the flag:
import { flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';

export const exampleFlag = flag({
  key: 'example-flag',
  adapter: vercelAdapter,
});

User targeting

import { dedupe, flag } from 'flags/next';
import { vercelAdapter } from '@flags-sdk/vercel';

type Entities = {
  team?: { id: string };
  user?: { id: string };
};

const identify = dedupe(async (): Promise<Entities> => ({
  team: { id: 'team-123' },
  user: { id: 'user-456' },
}));

export const exampleFlag = flag<boolean, Entities>({
  key: 'example-flag',
  identify,
  adapter: vercelAdapter,
});

Define the entities and attributes under Flags → Entities in the dashboard before you use them in rules or segments, and return the same names from identify (Entities). The example above allows --by user.id / --by team.id in vercel flags split, rollout, and rules add, and the matching conditions in dashboard rules. Entities are evaluated fresh on every call; a rule whose attribute is missing from the context is skipped. See How the CLI connects to the SDK.

Attribute types

Each attribute has a type: String, Number, Boolean, String Array, or Timestamp. The value identify returns must match it.

  • Timestamp is a Unix epoch in milliseconds as a number (Date.now(), user.createdAt.getTime(), 1719792000000). Do not pass a Date, an ISO string, or seconds.
  • Timestamp rules only compare: is after / is before / is at or after / is at or before (gt / lt / gte / lte). Equality, one-of, and exists operators are not available for Timestamp attributes.
  • Timestamp attributes cannot bucket a split or rollout (--by); use them in rule conditions only.
  • To release at a certain time, pass the current time as system.time and compare against it in a rule. Keep Date.now() inside dedupe so every flag in the request sees the same time:
const identify = dedupe(async () => ({
  system: { time: Date.now() },
  user: { id: 'user-456', signupAt: 1719792000000 },
}));

Flags Explorer

import { createFlagsDiscoveryEndpoint } from 'flags/next';
import { getProviderData } from '@flags-sdk/vercel';
import * as flags from '../../../../flags';

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return await getProviderData(flags);
});

Custom configuration

import { createVercelAdapter } from '@flags-sdk/vercel';

const customAdapter = createVercelAdapter(process.env.CUSTOM_FLAGS_KEY!);

export const exampleFlag = flag({
  key: 'example-flag',
  adapter: customAdapter,
});

Using your own client (e.g. for singleton)

If the app also uses @vercel/flags-core directly, create the client once and pass it to the adapter so both share the same instance:

import { createClient } from '@vercel/flags-core';
import { createVercelAdapter } from '@flags-sdk/vercel';

const vercelFlagsClient = createClient(); // Vercel OIDC token, on deployments and after `vercel env pull`
const vercelAdapter = createVercelAdapter(vercelFlagsClient);

export const exampleFlag = flag({
  key: 'example-flag',
  adapter: vercelAdapter,
});

Outside Vercel, pass the SDK key: createClient(process.env.FLAGS). Unlike vercelAdapter(), createClient() does not read FLAGS on its own.

Core client in other frameworks (for example, Express)

For frameworks without a Flags SDK entrypoint, use @vercel/flags-core directly. Create a shared client at module scope, but call evaluate() or bulkEvaluate() inside a request handler. Both initialize the client automatically; do not add a module-scope client.initialize() call or cache its promise for handlers to await.

// src/flags.ts
import { createClient } from '@vercel/flags-core';

const client = createClient();

// Call from a request handler, not during module loading.
export async function getVersion(): Promise<number> {
  const result = await client.evaluate<number>('version', 0);
  return result.value;
}

With Vercel OIDC, the token can come from request context and may not exist during module loading. Even embedded definitions require OIDC to select the entry by the token's project_id. Local .env.local credentials can hide this timing problem. Explicit initialization is optional and must wait until authentication is available; awaiting an already-started initialization promise later does not move it into request context.

vercel flags CLI

Manage Vercel Flags from the terminal with an authenticated CLI and a targeted project (Project targeting). SDK installation and vercel env pull are app-development steps, not CLI prerequisites (see Setup).

For the current subcommand list and options, run vercel flags --help or vercel flags <cmd> --help. For CLI-wide contracts (linking, --non-interactive, --yes, parsing stdout) follow the vercel-cli skill. This section covers only what --help cannot tell you.

How the CLI connects to the SDK
  • Key: the flag slug you pass to the CLI is the key in flag(). The flag returns one of the variants you created on Vercel (Run an A/B test).
  • Kind → type: inspect <key> prints the kind and variants. boolean → flag<boolean>(), string → flag<string>(), number → flag<number>(), json → flag<YourType>(). The kind is fixed at creation and cannot be changed (Flag types).
  • Variants → options: options on the declaration are optional. They give Flags Explorer a dropdown, pre-fill the dashboard when a draft is promoted, and let precompute serialize values and generatePermutations enumerate them (Declaring options). When you declare them, keep them equal to the variants inspect shows.
  • defaultValue: a flag that is archived, or declared in code but not created on Vercel (a draft), evaluates to defaultValue. Without defaultValue, evaluation throws (Archive, Drafts).
  • Targeting attributes: split, rollout, and rules add take --by <entity>.<attribute> (and --condition <entity>.<attribute>:<op>:<value>). Define the entity and attribute under Flags → Entities first, and return the same names from identify(); the docs use a User entity with an id attribute and --by user.id (Roll out a feature, Entities). When the attribute is missing from the context, a split or rollout serves its fallback variant (--default-variant in the CLI) and a rule that references it is skipped. Verify with evaluations.
  • Timestamp conditions: for Timestamp attributes, --condition accepts gt, lt, gte, lte or the aliases after, before, at-or-after, at-or-before with an ISO 8601 date-time or epoch milliseconds, for example --condition system.time:at-or-after:2026-04-16T09:00:00Z. after and before are exclusive; other operators are rejected. A date-time without a timezone uses the machine's local timezone, so include Z or an offset. --by rejects Timestamp attributes. See Attribute types.
  • Authentication: on Vercel, vercelAdapter() authenticates with the project's OIDC token and uses the configuration of the current environment; vercel env pull brings that credential to .env.local for local development. SDK keys (FLAGS) are for manual authentication: apps outside Vercel, custom environments, or flags owned by another project. Each SDK key is scoped to one environment and its full value is shown once, at creation (Getting started, SDK Keys).
  • Overrides: Flags Explorer stores overrides in a cookie signed with FLAGS_SECRET; flags declared with the SDK honour it automatically (Handling overrides). vercel flags override <key>=<value> produces the same token for the vercel-flag-overrides cookie and reads FLAGS_SECRET from the environment or .env.local, so use the secret of the environment you test against (see FLAGS_SECRET).
  • Embedded definitions: Vercel builds fetch the flag definitions once and bundle them into the deployment when the project uses @flags-sdk/vercel or @vercel/flags-core and the build can authenticate. This keeps every function on one snapshot and serves as the runtime fallback when the service is unreachable; opt out with VERCEL_FLAGS_DISABLE_DEFINITION_EMBEDDING=1 (Embedded definitions). vercel flags prepare is the same step for builds that run outside Vercel.
Lifecycle and safety

The docs describe these flows end to end: Roll out a feature, Run an A/B test. For cleanup, follow the deployment and evaluation checks below before archiving.

  • Promote: deploy the code to preview, enable or set the flag in preview, verify on the preview URL, deploy to production, then change production (enable, set, split, or rollout). Each environment keeps its own configuration; preview stays on its current value until you change it.
  • Serve vs define: enable / disable work on boolean flags only. set changes the served variant for any kind. update adds, removes, or renames variants and does not change what is served. A variant can only be removed when no environment configuration or rule references it, including rules that are stored but not active (Deleting a variant).
  • Static value vs targeting: set / enable / disable put the environment in static value mode; its split, rollout, and rules are preserved in the background. use-targeting switches back to targets and rules mode (Switching between static and rules modes). Run inspect first so you know what the environment serves today.
  • Confirm a change: inspect for the served state, versions for the change history (the dashboard can restore any earlier configuration), evaluations to confirm traffic reaches the new variant or to check whether a flag is still evaluated before archiving (Evaluation metrics). Local development evaluates the Development environment configuration.
  • Deploy removal first: search for the flag key and its camelCase name, remove the declaration and conditionals, verify in preview, and complete the rollout to production and every other environment using the flag. A merged PR or preview deployment is not sufficient. Older deployments can still evaluate it through Skew Protection: inspect the project's configured maximum age and account for longer-lived traffic, including crawler exceptions, rather than assuming a fixed 24 or 48 hours.
  • Verify no evaluations: before archiving, use vercel flags evaluations <flag> --since <window> --json with the explicit project/team target. Require no evaluations over an observation window after rollout that covers the applicable Skew Protection period. Check that the returned time range covers the window and the data is not truncated. Clients using @vercel/flags-core before 1.6.0 do not report evaluations, so verify reporting coverage; an empty result alone does not prove disuse (Evaluation metrics). If evaluations continue, reporting coverage is unknown, or the window has not elapsed, leave the flag active.
  • Archive before delete: only after those checks pass, archive the flag. unarchive restores its configuration and history. Keep it archived through the agreed observation period before permanent deletion; rm requires an archived flag and cannot be undone (Archive).
  • Agent runs: archive, unarchive, rm, and update --remove-variant prompt for confirmation. Pass --yes when the user has approved the action.

CLI reference: https://vercel.com/docs/cli/flags


Global Config

Package: @flags-sdk/global-config

pnpm i @flags-sdk/global-config

Env: GLOBAL_CONFIG="global-config-connection-string"

Usage

import { flag } from 'flags/next';
import { globalConfigAdapter } from '@flags-sdk/global-config';

export const exampleFlag = flag({
  adapter: globalConfigAdapter,
  key: 'example-flag',
});

Global Config should contain:

{
  "flags": {
    "example-flag": true,
    "another-flag": false
  }
}

Custom configuration

import { createGlobalConfigAdapter } from '@flags-sdk/global-config';

const myAdapter = createGlobalConfigAdapter({
  connectionString: process.env.OTHER_GLOBAL_CONFIG,
  options: {
    globalConfigItemKey: 'other-flags-key',
    teamSlug: 'my-team',
  },
});

Statsig

Package: @flags-sdk/statsig

pnpm i @flags-sdk/statsig

Env vars:

  • STATSIG_SERVER_API_KEY (required)
  • STATSIG_PROJECT_ID (optional)
  • EXPERIMENTATION_CONFIG (optional, Global Config)
  • EXPERIMENTATION_CONFIG_ITEM_KEY (optional)

Methods

import { statsigAdapter, type StatsigUser } from '@flags-sdk/statsig';

// Feature Gates
export const myGate = flag<boolean, StatsigUser>({
  key: 'my_feature_gate',
  adapter: statsigAdapter.featureGate((gate) => gate.value),
  identify,
});

// Dynamic Configs
export const myConfig = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_dynamic_config',
  adapter: statsigAdapter.dynamicConfig((config) => config.value),
  identify,
});

// Experiments
export const myExperiment = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_experiment',
  adapter: statsigAdapter.experiment((config) => config.value),
  identify,
});

// Autotune
export const myAutotune = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_autotune',
  adapter: statsigAdapter.autotune((config) => config.value),
  identify,
});

// Layers
export const myLayer = flag<Record<string, unknown>, StatsigUser>({
  key: 'my_layer',
  adapter: statsigAdapter.layer((layer) => layer.value),
  identify,
});

Same key, different mappings

Use . to distinguish flags from the same config:

export const text = flag<string, StatsigUser>({
  key: 'my_config.text',
  adapter: statsigAdapter.dynamicConfig((c) => c.value.text as string),
  identify,
});

export const price = flag<number, StatsigUser>({
  key: 'my_config.price',
  adapter: statsigAdapter.dynamicConfig((c) => c.value.price as number),
  identify,
});

Exposure logging

Disabled by default (middleware prefetch would cause premature exposures). Enable explicitly:

adapter: statsigAdapter.featureGate((gate) => gate.value, {
  exposureLogging: true,
})

Log exposures from the client instead when possible.

Flags Explorer

import { getProviderData as getStatsigProviderData } from '@flags-sdk/statsig';
import { mergeProviderData } from 'flags';

export const GET = createFlagsDiscoveryEndpoint(async () => {
  return mergeProviderData([
    getProviderData(flags),
    getStatsigProviderData({
      consoleApiKey: process.env.STATSIG_CONSOLE_API_KEY,
      projectId: process.env.STATSIG_PROJECT_ID,
    }),
  ]);
});

LaunchDarkly

Package: @flags-sdk/launchdarkly

pnpm i @flags-sdk/launchdarkly

Env vars:

  • LAUNCHDARKLY_CLIENT_SIDE_ID (required)
  • LAUNCHDARKLY_PROJECT_SLUG (required)
  • GLOBAL_CONFIG (required)

Usage

import { ldAdapter, type LDContext } from '@flags-sdk/launchdarkly';

const identify = dedupe((async ({ headers, cookies }) => {
  const user = await getUser(headers, cookies);
  return { key: user.userID };
}) satisfies Identify<LDContext>);

export const exampleFlag = flag<boolean, LDContext>({
  key: 'example-flag',
  identify,
  adapter: ldAdapter.variation(),
});

Flags Explorer

import { getProviderData as getLDProviderData } from '@flags-sdk/launchdarkly';

return mergeProviderData([
  getProviderData(flags),
  getLDProviderData({
    apiKey: process.env.LAUNCHDARKLY_API_KEY,
    projectKey: process.env.LAUNCHDARKLY_PROJECT_KEY,
    environment: process.env.LAUNCHDARKLY_ENVIRONMENT,
  }),
]);

PostHog

Package: @flags-sdk/posthog

pnpm i @flags-sdk/posthog

Env vars, always required:

  • POSTHOG_HOST (e.g. https://us.i.posthog.com or https://eu.i.posthog.com)
  • POSTHOG_PROJECT_API_KEY (phc_...)

Optional, opts into local evaluation (background polling) instead of remote:

  • POSTHOG_SECRET_KEY (phs_...)

For the Flags Explorer (getProviderData only):

  • POSTHOG_PERSONAL_API_KEY (phx_...)
  • POSTHOG_PROJECT_ID (e.g. 521742)

Methods

import { postHogAdapter } from '@flags-sdk/posthog';

// Value — boolean flag. Pass the adapter uninvoked or invoked, both work.
export const myFlag = flag<boolean>({
  key: 'my-flag',
  adapter: postHogAdapter,
  identify,
});

// Value — multivariate flag resolves to the variant string
export const myVariant = flag<string>({
  key: 'my-flag',
  adapter: postHogAdapter,
  identify,
});

// Payload
export const myPayload = flag({
  key: 'my-flag',
  adapter: postHogAdapter.payload,
  defaultValue: {},
  identify,
});

identify must return { distinctId }.

Flags Explorer

Requires: POSTHOG_PERSONAL_API_KEY, POSTHOG_PROJECT_ID

import { getProviderData as getPostHogProviderData } from '@flags-sdk/posthog';

export const GET = createFlagsDiscoveryEndpoint(() =>
  getPostHogProviderData({
    personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY!,
    projectId: process.env.POSTHOG_PROJECT_ID!,
  }),
);

GrowthBook

Package: @flags-sdk/growthbook

pnpm i @flags-sdk/growthbook

Env: GROWTHBOOK_CLIENT_KEY (required)

Usage

import { growthbookAdapter, type Attributes } from '@flags-sdk/growthbook';

const identify = dedupe((async ({ cookies }) => ({
  id: cookies.get('user_id')?.value,
})) satisfies Identify<Attributes>);

export const myFlag = flag({
  key: 'my_feature',
  identify,
  adapter: growthbookAdapter.feature<boolean>(),
});

Global Config

Set GROWTHBOOK_EDGE_CONNECTION_STRING or EXPERIMENTATION_CONFIG (Vercel Marketplace).

Tracking

growthbookAdapter.setTrackingCallback((experiment, result) => {
  after(async () => {
    console.log('Experiment', experiment.key, 'Variation', result.key);
  });
});

Flagsmith

Package: @flags-sdk/flagsmith

pnpm i @flags-sdk/flagsmith

Env: FLAGSMITH_ENVIRONMENT_KEY (required)

Remote evaluation is the default for serverless deployments. For a long-running server, use createFlagsmithAdapter({ environmentKey, enableLocalEvaluation: true }) with a server-side key to opt into local evaluation and environment polling. Call adapter.close() on shutdown to stop polling.

Usage with type coercion

import { flagsmithAdapter } from '@flags-sdk/flagsmith';

export const buttonColor = flag<string>({
  key: 'button-color',
  defaultValue: 'blue',
  adapter: flagsmithAdapter.getValue({ coerce: 'string' }),
});

export const showBanner = flag<boolean>({
  key: 'show-banner',
  defaultValue: false,
  adapter: flagsmithAdapter.getValue({ coerce: 'boolean' }),
});

Coercion options: 'string', 'number', 'boolean', or omit for raw value.


Reflag

Package: @flags-sdk/reflag

pnpm i @flags-sdk/reflag

Env: REFLAG_SECRET_KEY

import { reflagAdapter, type Context } from '@flags-sdk/reflag';

const identify = dedupe((async ({ headers, cookies }) => ({
  user: { id: 'user-id', name: 'name', email: 'email' },
  company: { id: 'company-id' },
})) satisfies Identify<Context>);

export const myFeature = flag<boolean, Context>({
  key: 'my_feature',
  identify,
  adapter: reflagAdapter.isEnabled(),
});

OpenFeature

Package: @flags-sdk/openfeature + @openfeature/server-sdk

pnpm i @flags-sdk/openfeature @openfeature/server-sdk

Setup

import { createOpenFeatureAdapter } from '@flags-sdk/openfeature';

// Sync provider
OpenFeature.setProvider(new YourProvider());
const adapter = createOpenFeatureAdapter(OpenFeature.getClient());

// Async provider
const adapter = createOpenFeatureAdapter(async () => {
  await OpenFeature.setProviderAndWait(new YourProvider());
  return OpenFeature.getClient();
});

Methods

adapter.booleanValue()  // boolean flags
adapter.stringValue()   // string flags
adapter.numberValue()   // number flags
adapter.objectValue()   // object flags

All require defaultValue on the flag declaration.


Split

Package: @flags-sdk/split (Flags Explorer only, adapter coming soon)

import { getProviderData as getSplitProviderData } from '@flags-sdk/split';

getSplitProviderData({
  adminApiKey: process.env.SPLIT_ADMIN_API_KEY,
  environmentId: process.env.SPLIT_ENVIRONMENT_ID,
  organizationId: process.env.SPLIT_ORG_ID,
  workspaceId: process.env.SPLIT_WORKSPACE_ID,
});

Optimizely

Package: @flags-sdk/optimizely (Flags Explorer only, adapter coming soon)

import { getProviderData as getOptimizelyProviderData } from '@flags-sdk/optimizely';

getOptimizelyProviderData({
  projectId: process.env.OPTIMIZELY_PROJECT_ID,
  apiKey: process.env.OPTIMIZELY_API_KEY,
});

Custom Adapters

Create an adapter factory:

import type { Adapter } from 'flags';

export function createMyAdapter(/* options */) {
  return function myAdapter<ValueType, EntitiesType>(): Adapter<ValueType, EntitiesType> {
    return {
      origin(key) {
        return `https://my-provider.com/flags/${key}`;
      },
      async decide({ key }): Promise<ValueType> {
        return false as ValueType;
      },
    };
  };
}

Bulk evaluation (bulkDecide)

Adapters can implement an optional bulkDecide hook. When set (and the adapter has an adapterId), evaluate() calls it once for every group of flags that share this adapter and the same identify source — instead of calling decide per flag. This lets the provider share work across evaluations (e.g. a single network request for many flags).

return {
  adapterId: 'my-provider', // required for bulkDecide to be used
  origin(key) {
    return `https://my-provider.com/flags/${key}`;
  },
  async decide({ key }): Promise<ValueType> {
    return false as ValueType;
  },
  // Called by evaluate() for a batch of flags sharing this adapter + identify
  async bulkDecide({ flags, entities, headers, cookies }) {
    // flags: { key: string; defaultValue?: unknown }[]
    // Return a record keyed by flag key.
    return Object.fromEntries(
      flags.map(({ key }) => [key, false as ValueType]),
    );
  },
};

Contract:

  • Return Record<flagKey, value>. Missing keys or value: undefined fall back to each flag's defaultValue.
  • Throwing falls back to defaultValue per flag (and rejects for flags without a defaultValue).
  • A flag with an inline decide takes precedence and is excluded from bulk evaluation.

Default adapter pattern

Expose a lazily-initialized default for simpler usage:

let defaultAdapter: ReturnType<typeof createMyAdapter> | undefined;

export function myAdapter<V, E>(): Adapter<V, E> {
  if (!defaultAdapter) {
    if (!process.env.MY_API_KEY) throw new Error('Missing MY_API_KEY');
    defaultAdapter = createMyAdapter(process.env.MY_API_KEY);
  }
  return defaultAdapter<V, E>();
}

Usage:

import { myAdapter } from './my-adapter';

export const exampleFlag = flag({
  key: 'example',
  adapter: myAdapter,
});

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at ce31fdf. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 9 hours ago.

Activeupdated 13 hours ago
Other metadata
summary
Flags SDK guidance — declare flags with flag(), connect provider adapters, manage Vercel Flags via the vercel flags CLI, precompute static variants, and set up the Flags Explorer.
metadata
{
  "priority": 6,
  "docs": [
    "https://vercel.com/docs/flags",
    "https://flags-sdk.dev"
  ],
  "sitemap": "https://vercel.com/sitemap.xml",
  "pathPatterns": [
    "flags.ts",
    "flags.tsx",
    "lib/flags.ts",
    "src/flags.ts",
    "lib/flags/**",
    "src/flags/**",
    ".well-known/vercel/flags/**"
  ],
  "importPatterns": [
    "flags/next",
    "flags/sveltekit",
    "flags/react",
    "flags",
    "@flags-sdk/*",
    "@vercel/flags"
  ],
  "bashPatterns": [
    "\\bvercel\\s+flags\\b",
    "\\bvc\\s+flags\\b",
    "\\bnpm\\s+(install|i|add)\\s+[^\\n]*\\bflags\\b",
    "\\bpnpm\\s+(install|i|add)\\s+[^\\n]*\\bflags\\b",
    "\\bbun\\s+(install|i|add)\\s+[^\\n]*\\bflags\\b",
    "\\byarn\\s+add\\s+[^\\n]*\\bflags\\b",
    "\\bnpm\\s+(install|i|add)\\s+[^\\n]*@flags-sdk/",
    "\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@flags-sdk/",
    "\\bbun\\s+(install|i|add)\\s+[^\\n]*@flags-sdk/",
    "\\byarn\\s+add\\s+[^\\n]*@flags-sdk/"
  ],
  "promptSignals": {
    "phrases": [
      "feature flag",
      "feature flags",
      "flags sdk",
      "vercel flags",
      "flags explorer",
      "feature gate",
      "feature gating",
      "a/b test",
      "a/b testing",
      "ab test",
      "ab testing",
      "flag rollout",
      "gradual rollout",
      "percentage rollout",
      "kill switch",
      "flag variant",
      "flag adapter",
      "precompute flags",
      "traffic split",
      "targeting rules",
      "flag rules",
      "flag segments",
      "flag targeting",
      "flag evaluations",
      "flag overrides",
      "existing flag"
    ],
    "allOf": [
      [
        "flag",
        "rollout"
      ],
      [
        "flag",
        "variant"
      ],
      [
        "flag",
        "toggle"
      ],
      [
        "flag",
        "experiment"
      ],
      [
        "experiment",
        "variant"
      ],
      [
        "split",
        "test"
      ]
    ],
    "anyOf": [
      "flag",
      "experiment",
      "experimentation",
      "rollout",
      "variant"
    ],
    "noneOf": [
      "command-line flag",
      "command line flag",
      "cli flag",
      "compiler flag",
      "flag emoji",
      "rolling release"
    ],
    "minScore": 6
  }
}
retrieval
{
  "aliases": [
    "feature flags",
    "feature toggles",
    "vercel flags",
    "flags sdk",
    "a/b testing"
  ],
  "intents": [
    "add a feature flag",
    "run an a/b test",
    "gate a feature",
    "roll out gradually",
    "manage flags from cli"
  ],
  "entities": [
    "Flags SDK",
    "Vercel Flags",
    "Flags Explorer",
    "vercelAdapter",
    "precompute",
    "FLAGS_SECRET"
  ]
}
chainTo
[
  {
    "pattern": "precompute\\s*\\(|generatePermutations|flags/next.*precompute",
    "targetSkill": "routing-middleware",
    "message": "Precompute pattern detected — loading Routing Middleware guidance for the middleware rewrites that serve static flag variants."
  },
  {
    "pattern": "@flags-sdk/(edge|global)-config|create(Edge|Global)ConfigAdapter",
    "targetSkill": "vercel-storage",
    "message": "Global Config flag adapter detected — loading Vercel storage guidance for Global Config setup and limits."
  }
]

README badge

README badge for vercel-labs/vercel-plugin/flags-sdk