This skill helps you implement, manage, and maintain feature flags following best practices.
/plugin marketplace add cameronsjo/claude-marketplace/plugin install dx@cameronsjoThis skill inherits all available tools. When active, it can use any tool Claude has access to.
README.mdpackage-lock.jsonpackage.jsonresources/flag-config-schema.jsonresources/flag-template.tsresources/test-template.tsscripts/create-flag-config.tsscripts/detect-stale-flags.tsscripts/flag-coverage.tsscripts/generate-flag.tstsconfig.jsonThis skill helps you implement, manage, and maintain feature flags following best practices.
This skill provides comprehensive guidance and automation for:
Enable incomplete features in production while under development.
Usage: Trunk-based development, dark launches, gradual rollouts
Lifecycle: Remove after 100% rollout and old code path deleted
A/B testing and experimentation.
Usage: Compare variants for metrics and optimization
Lifecycle: Remove after experiment concludes and winning variant implemented
Circuit breakers and operational controls.
Usage: Kill switches, external service toggles, performance tuning
Lifecycle: Permanent, reviewed quarterly
Access control and entitlements.
Usage: Feature gating by plan/tier, beta access
Lifecycle: Permanent, part of product offering
Always use positive naming to avoid confusing double negatives.
// ✅ Good: Positive logic, clear intent
if (featureFlags.isEnabled('new-checkout')) { /* ... */ }
if (featureFlags.isVisible('banner')) { /* ... */ }
if (featureFlags.isActive('dark-mode')) { /* ... */ }
// ❌ Bad: Negative logic leads to confusion
if (!featureFlags.isDisabled('new-checkout')) { /* ... */ } // Double negative
if (featureFlags.isHidden('banner')) { /* ... */ } // Requires negation
Rule: Name flags and methods so the "true" case is the desired/enabled state.
// ✅ Good: Clear what it does
FeatureFlag.NewCheckoutFlow
FeatureFlag.AIAssistantEnabled
FeatureFlag.AdvancedAnalyticsVisible
// ❌ Bad: Vague or negative
FeatureFlag.Toggle1
FeatureFlag.DisableLegacyCheckout
FeatureFlag.NotV1
Use enums for type-safety and refactor-friendliness:
export enum FeatureFlag {
NewCheckout = 'new-checkout-flow',
AIChat = 'ai-chat-assistant',
DarkMode = 'dark-mode-ui',
}
interface FeatureFlagService {
isEnabled(flag: FeatureFlag, context?: FlagContext): boolean;
getVariant(flag: FeatureFlag, context?: FlagContext): string;
}
Benefits: Type-safety, autocomplete, refactor-friendly, prevents typos
Make flags injectable for testability:
class CheckoutService {
constructor(private featureFlags: FeatureFlagService) {}
async processCheckout(cart: Cart): Promise<Order> {
if (this.featureFlags.isEnabled(FeatureFlag.NewCheckout)) {
return this.newCheckoutFlow(cart);
}
return this.legacyCheckoutFlow(cart);
}
}
Benefits: Testable, mockable, clear dependencies
Always validate flag configuration:
interface FlagConfiguration {
flags: {
[key in FeatureFlag]: {
enabled: boolean;
rolloutPercentage?: number;
allowedUsers?: string[];
allowedOrgs?: string[];
enabledEnvironments?: string[];
};
};
}
Every flag must have tests for both enabled and disabled states:
describe('CheckoutService', () => {
it('uses new checkout when flag enabled', async () => {
const mockFlags = { isEnabled: () => true };
const service = new CheckoutService(mockFlags);
const order = await service.processCheckout(cart);
expect(order.version).toBe('v2');
});
it('uses legacy checkout when flag disabled', async () => {
const mockFlags = { isEnabled: () => false };
const service = new CheckoutService(mockFlags);
const order = await service.processCheckout(cart);
expect(order.version).toBe('v1');
});
});
Test both code paths in E2E tests:
describe('Checkout flow', () => {
describe('with new-checkout-flow enabled', () => {
beforeEach(() => setFlag('new-checkout-flow', true));
// tests for new path
});
describe('with new-checkout-flow disabled', () => {
beforeEach(() => setFlag('new-checkout-flow', false));
// tests for legacy path
});
});
When creating a new flag:
false (disabled)Set removal date when flag is created. Stale flags are technical debt.
When removing a flag:
Schedule: Review all flags quarterly, mark stale flags for removal.
Problem: Hundreds of stale flags accumulate over time Solution: Regular cleanup, automated staleness detection, removal deadlines
// ❌ Bad: Hard to reason about
if ((flagA && flagB) || (!flagA && flagC)) { }
// ✅ Good: Single flag with clear meaning
if (featureFlags.isEnabled(FeatureFlag.ComplexFeature)) { }
Problem: Flags depend on each other Solution: Create composite flags or refactor architecture
Problem: Old code path breaks when flag is enabled Solution: Test both paths in CI, enforce with coverage tooling
Problem: Cannot measure impact or usage Solution: Add metrics from day one
// ❌ Bad: Leaks upcoming features
return res.json(allFlags);
// ✅ Good: Only return evaluated flags
const evaluatedFlags = {
newCheckout: featureFlags.isEnabled('new-checkout', user),
};
return res.json(evaluatedFlags);
This skill includes automation scripts in /scripts/:
Generate complete flag implementation with tests.
npm run generate-flag -- --name NewCheckoutFlow --type release --description "New checkout experience"
Generates:
Find flags that haven't been modified recently.
npm run detect-stale -- --days 90
Identifies:
Verify test coverage for both code paths.
npm run flag-coverage
Reports:
Generate flag configuration from schema.
npm run create-flag-config -- --flag NewCheckoutFlow --type release
Creates validated configuration with:
Complete TypeScript flag implementation template including:
JSON schema for flag configuration validation covering:
Comprehensive test template with:
unleash-client)detect-stale-flags.tslogger.info('Feature flag evaluated', {
flag: FeatureFlag.NewCheckout,
enabled: true,
userId: user.id,
evaluationTime: Date.now(),
});
Always validate flag configuration:
const rollout = config.flags[flag].rolloutPercentage;
if (rollout < 0 || rollout > 100) {
throw new Error(`Invalid rollout percentage: ${rollout}`);
}
Log changes to sensitive flags:
auditLog.record({
action: 'FLAG_CHANGED',
flag: FeatureFlag.AdminPanel,
oldValue: false,
newValue: true,
changedBy: user.id,
timestamp: Date.now(),
});
Never expose all flags to client. Only send evaluated results:
// ✅ Good: Only evaluated flags for current user
const evaluatedFlags = {
newCheckout: featureFlags.isEnabled('new-checkout', user),
darkMode: featureFlags.isEnabled('dark-mode', user),
};
return res.json(evaluatedFlags);
npm run generate-flag -- --name NewPaymentFlow --type release --description "Updated payment processing" --owner @yourteam --removal-date 2025-12-31
if (this.flags.isEnabled(FeatureFlag.NewPaymentFlow)) {
return this.processPaymentV2(order);
}
return this.processPaymentV1(order);
Add tests for both paths using generated template
Deploy with flag disabled, enable gradually
Set calendar reminder for removal date
npm run detect-stale -- --days 90 --type release
Review flagged items with team
For each flag to remove:
npm run flag-coverage
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.