In the previous chapter, Voice Configuration Feedback, we added helpful hints to guide the user on how to use the voice feature.
We have built a fully functional feature! But now we face a safety and business challenge.
Imagine we release the Voice feature, but suddenly we discover a critical bug that causes the application to crash for everyone.
If the code is already on the user's computer, how do we stop them from using it? We can't reach into their computer and delete the file. Usually, we would have to release a new version of the app, which takes time.
We need a faster way. We need a "Remote Control" to turn the feature off instantly, everywhere in the world, without the user updating their app.
This pattern is called Feature Availability Gating.
Think of your command like a Nightclub:
Even if the club is open (the code exists), the Bouncer checks two things:
First, we don't want to write these complex checks inside every single file. We create a central "Rule Book" file.
We call this voiceModeEnabled.ts.
// voice/voiceModeEnabled.ts
// The remote switch (GrowthBook)
import { isVoiceGrowthBookEnabled } from './growthBook.js'
// The user ID check (Auth)
import { isAnthropicAuthEnabled } from '../utils/auth.js'
export const isVoiceModeEnabled = () => {
// Rule 1: Is the remote switch ON?
if (!isVoiceGrowthBookEnabled()) {
return false
}
// Rule 2: Is the user logged in?
if (!isAnthropicAuthEnabled()) {
return false // Gate closed
}
return true // Gate open
}
isVoiceGrowthBookEnabled(): Checks a remote server configuration. If we toggle a switch on our server dashboard, this returns false instantly for all users.isAnthropicAuthEnabled(): Checks if the user has signed in.Now we go back to our Command Definition Pattern from Chapter 1.
If the Bouncer says the club is closed, we shouldn't even show the sign on the street. We should hide the command from the CLI's help menu so users don't try to click it.
// index.ts
import { isVoiceModeEnabled } from '../../voice/voiceModeEnabled.js'
const voice = {
name: 'voice',
description: 'Toggle voice mode',
// The magic property
get isHidden() {
// If voice mode is NOT enabled, hide this command.
return !isVoiceModeEnabled()
},
// ... other properties
}
get isHidden(): The CLI checks this property every time it renders the menu. If isVoiceModeEnabled() returns false (because of the kill-switch or auth), the command vanishes from the UI.Sometimes, a user might know the command exists even if it is hidden (e.g., they wrote a script yesterday). They might try to force their way past the Bouncer.
So, we place a second guard right inside the command execution logic.
// voice.ts
import { isVoiceModeEnabled } from '../../voice/voiceModeEnabled.js'
export const call = async () => {
// FINAL CHECK: Stop right here if not allowed.
if (!isVoiceModeEnabled()) {
return {
type: 'text',
value: 'Voice mode is currently unavailable.',
}
}
// ... continue to load microphone logic ...
}
This ensures that even if someone finds the "Back Door," the security system still stops them.
Let's visualize the flow when a user opens the application.
Guard (our logic) before showing or running anything.Here is how we handle the nuanced "Auth Hint" logic in the actual implementation.
Sometimes, if the feature is on but the user just isn't logged in, we don't want to hide it completelyβwe want to tell them to log in!
// voice.ts (Implementation Detail)
if (!isVoiceModeEnabled()) {
// Case A: User just needs to log in
if (!isAnthropicAuthEnabled()) {
return {
type: 'text',
value: 'Voice mode requires an account. Run /login.',
}
}
// Case B: Kill-switch is active (Feature is dead)
return {
type: 'text',
value: 'Voice mode is not available.',
}
}
In this final chapter, you learned Feature Availability Gating.
Congratulations! You have built a complete, production-grade CLI feature.
You now possess the toolkit to build robust, user-friendly, and controllable CLI applications. Happy coding!
Generated by Code IQ