Use when the user asks to remove, retire, deprecate or clean up a feature flag or feature toggle, says a flag is fully rolled out, or runs /flagrm-remove <flag>, in a repository with a flagrm.config.yaml. Works for Angular, .NET and other stacks: IFeatureManager, [FeatureGate], appsettings FeatureManagement, LaunchDarkly-style calls, flags objects, enums, signals, *ngIf/@if templates.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
SSkills Directory ProSkills Directory
Get any skill into Claude in one click.
Are you the author of Flagrm Remove?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/dino-cosic-flagrm-remove)
---
name: flagrm-remove
description: >
Use when the user asks to remove, retire, deprecate or clean up a feature flag
or feature toggle, says a flag is fully rolled out, or runs /flagrm-remove <flag>,
in a repository with a flagrm.config.yaml. Works for Angular, .NET and other
stacks: IFeatureManager, [FeatureGate], appsettings FeatureManagement,
LaunchDarkly-style calls, flags objects, enums, signals, *ngIf/@if templates.
---
# Remove a feature flag
**The flag is permanently ON.** Keep the ON path, delete the OFF path and everything only it used. Behavior with the flag ON must not change.
You find and edit the code. `flagrm` records the state before you start and
decides when you are done: **done means `flagrm verify <flag> --json` exited 0.**
Run it as `npx --no-install flagrm`, one command per call, no `||` (plain `npx flagrm` is another package).
Several flags (`/flagrm-remove A B C`): read [batch.md](batch.md) first; it runs this workflow once per flag.
## 1. Preconditions
- `flagrm doctor` must not warn about uncommitted files (flagrm's own setup files and `exclude`d paths don't count). Otherwise ask the user to commit or stash.
- No `flagrm.config.yaml`: run `flagrm init` and fill in project paths and build/test commands with the user.
Its doctor reports a missing SDK, `cargo` or Docker: show it. For `cargo`/Docker, offer `flagrm scope`; `--write` only on a yes.
- `flagrm list --json`: find the flag in `flags[]`. Not there: show the closest names and ask.
- `state` is `off`, `mixed` or `conditional`: name the environments in `config[]` that change and get a yes, or stop.
`unconfigured` (set in a flag service): with `launchdarkly:` in the config, `flagrm stale <flag> --json`; verdict `remove` confirms ON, another verdict: stop and show its `reasons`. No section, or `stale` exits non-zero (show its message): ask if it is ON everywhere. The removal request is not that answer.
- Read `CLAUDE.md` or `AGENTS.md` if present, for conventions and build commands.
## 2. Baseline
`flagrm baseline <flag> --json` records the git commit, build and test results and the flag's `names[]`.
A `checks[]` entry with a non-zero `exitCode` and a `failure`: a setup problem. Show the user `failure.message` (and `flagrm scope`); once fixed, rerun with `--force`.
Without `failure`: a build, stop. Tests: stop if the failures (`results.failedTests`) could cover the flag's code; else ask the user to accept them as known, and on a yes run `flagrm baseline <flag> --accept failures`.
## 3. Find every usage
- Grep for each name in `names[]`: code, templates, config and tests. `parameterizedTests[]` are test calls passing ON/OFF.
- `suggestedNames[]`: values of the flag with generic names. Record each that carries only this flag, a local, parameter or field with `--file <its file>` (repeat `--file` for each file; a component's template is added for you).
- Follow the value further: a method or property returning it, a field or local holding it, a parameter
it's passed as, a DI-registered bool. Record each so `verify` checks it, then grep for it:
`flagrm baseline <flag> --name IsNewCheckoutEnabledAsync --kind wrapper` (`--kind alias` for a constant like `Flags.NewCheckout`).
## 4. Remove
Read `patterns.md` in this skill's directory when a case is unclear.
- Condition: keep the ON branch, delete the OFF branch, drop the check.
`a && flag` → `a`, `a || !flag` → `a`. Never touch other flags.
- Wrapper: replace each call with `true` and simplify, then delete the wrapper, its interface member and DI registration.
- Passed as a parameter: pass `true` and simplify the callee. Once no caller
passes anything else, remove the parameter and the argument at every caller.
- Code only the OFF path used (methods, classes, fields, constructor parameters,
DI registrations, imports/usings, files): delete it. Keep what anything else uses.
- Definition and config: remove the constant, enum member or flags-object key,
and the key in every environment's config file. Remove a section left empty.
Keep a key the service sends to other apps (a known-flags list behind a config endpoint): they'd read OFF.
- Tests: delete tests of the OFF path. In the others, remove the flag setup
(mocks, spies, overrides) and drop the flag from the test name. A test
parameterized over ON/OFF keeps only the ON case.
- Keep the flag system itself (the flag service or client and its generic methods), public API
of a library package, and anything that also defines other flags. Mention them in your report.
## 5. Verify until it passes
`flagrm verify <flag> --json`. Fix every check with `status: "fail"`, run it again.
| Check | Fix |
|---|---|
| `leftovers` | Remove each reference listed. A comment is a warning: reword or delete it. A test checking text you deleted tests the OFF path: delete it. ON text no test checks: say so in your report |
| `dead-code` | Delete what the removal left unused. A declaration "referenced only where it is declared" (warning): delete it if only the OFF path used it; else say why it stays |
| `build` | Fix the error your edit caused; don't touch unrelated code. `tsc` skips templates: grep `.html` for each member you removed or renamed |
| `tests` | A new failure means ON behavior changed: fix the edit, not the test. Known failures (failed at the baseline) only warn. A test listed as no longer running (`unexplained`) must be one you removed on purpose: say why. A changed test file whose tests didn't run: run them yourself and report it |
| `lint` | Fix what your edit caused. An already-failing lint fails only on new error lines: fix those; other new lines it lists only warn |
| `coverage` | Tell the user which changed files no command checks; don't add or change commands yourself |
Exit 2 is a usage or config error: read the message. `--skip build,tests,lint,coverage` is fine for a quick check in between; only a full run counts, and the Stop hook won't let you finish on one with `--skip`. After three failed attempts on one check, show the user the findings. Don't run code generators or formatters after the passing verify; if you must, verify again.
## 6. Hand back
1. `flagrm verify <flag> --md` on the final tree (it reuses the passing build, test and lint runs; edit nothing after it): show its output exactly as printed; don't reword it or change its numbers. It must pass: the Stop hook checks it.
2. **Notes**, 3–5 lines: what you kept on purpose, each remaining warning with its reason, follow-ups in the flag service. If `state` wasn't `on` and nobody confirmed ON, start with "Assumed ON everywhere". End with: optional, for an independent review, run `/flagrm-review <flag>` in a new session.
3. The commit message:
<!-- commit-message:start -->
**Proposed commit message**, in a code block:
```
chore: remove <flag> feature flag
<one or two sentences: which path stays and what was deleted>
```
- Follow the repository's convention instead when `git log --oneline -10` shows a different one.
- A ticket in the branch name (`feature/feat-24586`): add it the way the log does (`#24586`, `FEAT-24586`), else `(#24586)` after the subject.
- Wrap the body at 72 columns.
<!-- commit-message:end -->
Commit only when the user asked (in a batch: their answer at the start).
## Rules
- Never lift code out of the OFF path into live code.
- Match the file's formatting; don't refactor unrelated code.
- Never edit `.flagrm/`. Rerun `flagrm baseline <flag>` only with `--name`, or with `--force` before your first edit (it refuses after).
- Never `--skip` a failing check, and never change a test's expected value or the build, test or lint commands or `covers` to pass. If the user changes the config mid-removal (say, to leave out tests that need services), run `flagrm baseline <flag> --accept config` once they confirm.
- `baseline` and `verify` reuse a passing verify's build, test and lint results on the same tree and commands. If the user changed the machine (SDK, Docker, services) since, add `--no-reuse`.