Remove feature flags with your AI coding agent.

The agent deletes the flag and the code only its OFF path used. flagrm checks the build, the tests and every leftover reference, and won't let the agent stop until they pass.

npm install -D flagrm

Read the quick start Source on GitHub

Works with Claude Code, GitHub Copilot and Codex. Angular and .NET out of the box, any other stack through the generic adapter. Open source, Node 22+.

~/shop — claude

How a removal runs

The agent does the reasoning: finding the flag, its aliases and wrappers, deleting the OFF path, refactoring methods the flag was passed into. flagrm decides when the work is done.

  1. npx flagrm init

    Asks which agents to set up, with the ones your repository already uses pre-selected, and installs only their files: the flagrm-remove and flagrm-verify skills, the Claude Code Stop hook, Copilot prompt files, an AGENTS.md pointer for Codex. Writes flagrm.config.yaml for the Angular and .NET projects it finds and adds a .gitignore entry. It never overwrites a file.You run this once.

  2. /flagrm-remove NewCheckout

    On a clean tree the agent runs flagrm baseline, which records the commit, build and test results and the flag's names. In .NET code the baseline also follows the flag's value into locals, fields, parameters and methods that carry it. The agent searches for every usage, records wrappers it finds with --name, removes the flag, and runs flagrm verify until it passes.You ask, the agent works.

  3. flagrm hook stop

    If the agent tries to finish after editing tracked files without a passing verify, the Stop hook sends it back to work with the reason. A removal paused on a question to you doesn't block.Claude Code only. On other agents, verify is still the gate.

  4. /flagrm-verify NewCheckout

    You get the checks, the files changed since the baseline, the tests deleted or renamed, and a proposed commit message. The agent never commits for you.You review and commit.

What verify checks

In typed code, deleting the flag's definition turns every missed reference into a compile error, so the build does most of the work. The other checks cover what the compiler can't see.

CheckFails when
leftoversA recorded name is still in code or config: the flag literal, its constants, the wrappers the agent recorded. A mention in a comment is a warning, and so is a test that still checks text the removal deleted: a test of the OFF path that never names the flag.
dead-codeThe compiler reports new unused locals, imports or private members in the changed files.
buildA project's build command fails.
testsA test that passed at the baseline now fails. Tests that already failed at the baseline only warn. Tests that no longer run are sorted by why: deleted in the diff, renamed without the flag in the name, or left out by a changed test command. Any other is a warning the agent has to explain.

The baseline also records the build and test commands and the config it ran with. If they change before verify, the affected check warns and shows the difference. Exit code 0 on pass, 1 on fail. Add --strict to fail on warnings too, or --json to wire it into CI.

Supported stacks

An adapter tells flagrm how to find flags, build, test and read the compiler's unused-code warnings. Angular and .NET have their own. The generic adapter covers any other language through file globs and the names of your flag-evaluation methods.

AdapterFinds flags inDead-code checkDefault build and test
angularFlag objects and enums named like *Flag*, *Feature* or *Toggle*, the features of environment*.ts, and calls to the configured methodsTypeScript's unused locals and parameters, on the changed files onlynpx ng build, npx ng test --watch=false
dotnetIFeatureManager, [FeatureGate], FeatureManagement in appsettings*.json, flag constants, and the locals, fields, parameters and methods the flag's value flows intoCompiler warnings for unused fields, locals and parameters, and the IDE analyzers when the build enforces code styledotnet build --no-incremental, dotnet test
genericString literals passed to the configured methods, constants holding them, and FeatureManagement keys in JSON. Go, Python, Java, Kotlin, Rust, Ruby, PHP, JS and more by defaultNot available: leftovers, build and tests still runInferred from go.mod, Cargo.toml or package.json scripts

Per-test comparison against the baseline reads JUnit XML or TRX from testResults. Without it, tests compares exit codes. Flags that live only in a flag service, such as LaunchDarkly, show up through the calls that evaluate them. The skill asks you to confirm the flag is ON everywhere before it starts.

One file of config

init fills in what it can detect. You add the build and test commands, then check them with npx flagrm doctor --run.

Point testResults at JUnit XML or TRX and verify compares individual tests against the baseline, not just the exit code.

Machine can't build everything? doctor checks the .NET SDK global.json asks for, cargo and Docker. npx flagrm scope --write creates a solution filter that leaves out what can't build and points the build and test commands at it.

flagrm.config.yamltools: [claude-code, codex]      # agents init and update set up
projects:
  - name: web
    adapter: angular
    path: ./frontend
    build: npm run build
    test: npx ng test --watch=false
    testResults: ./frontend/test-results/junit.xml
    methods: [isEnabled, watch]   # how flags are evaluated
  - name: api
    adapter: dotnet
    path: ./backend
    build: dotnet build --no-incremental
    test: dotnet test --logger trx --results-directory TestResults
    testResults: ./backend/TestResults/*.trx
  - name: service
    adapter: generic              # any other stack
    path: ./service
    globs: ['**/*.go', 'config/**/*.yaml']
exclude: ['**/Migrations/**']

Get started

You need Node.js 22 or later, git, and an AI coding agent that reads skills: Claude Code, GitHub Copilot or Codex. The Stop hook is Claude Code only.

Commit or stash first: the baseline starts from a clean working tree, and verify compares against that commit.

The full command reference is in the README. New to flag removal? Read how to remove a feature flag safely.

terminal# install and set up your agents
npm install -D flagrm
npx flagrm init

# add build and test commands to flagrm.config.yaml, then
npx flagrm doctor --run

# in your agent
/flagrm-remove NewCheckout

# review the result and the proposed commit message
/flagrm-verify NewCheckout

Questions

What flagrm does, what it leaves to the agent, and what it can't see.

Does flagrm edit my code?
No. Your agent finds the usages and makes the edits, guided by the flagrm-remove skill. flagrm records the baseline, runs the checks and decides when the removal is done. Nothing is committed: you review the diff and commit it yourself.
Does a passing verify mean the removal is correct?
It means no recorded name is left in code or config, the build passes, no test that passed at the baseline fails, and the compiler reports no new unused code. That's as good as your tests and build are. Review the diff the way you would any other change.
What can't it catch?
Flag names built at runtime (string concatenation, reflection), wrappers the agent didn't find and record, keys outside the repository (Key Vault, Helm values, App Service settings) and other services that read the same flag. In untyped code, missed references don't break the build, so leftovers and your tests carry more of the weight.
Does it work with LaunchDarkly, Unleash or my own flag service?
flagrm doesn't connect to a flag service. It works on the code: set methods to the names of your evaluation calls (isEnabled, variation, …) and it finds the flags passed to them. Archive the flag in the service after the code change is deployed, not before.
Which agents does init set up?
Claude Code gets the skills in .claude/skills/ and the Stop hook. Copilot gets the skills in .github/skills/ and /flagrm-remove and /flagrm-verify prompt files. Codex gets the skills in .agents/skills/ and a pointer in AGENTS.md. init never overwrites a file.
Is it stable?
flagrm is pre-1.0 and Apache 2.0 licensed. A minor release may contain breaking changes. They're listed in the changelog.