Skip to content

feat(remove): add --force to skip the confirmation prompt - #594

Open
SaulMoro wants to merge 1 commit into
Tencent:mainfrom
SaulMoro:feat/remove-force-flag-591
Open

SaulMoro wants to merge 1 commit into
Tencent:mainfrom
SaulMoro:feat/remove-force-flag-591

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Summary

askConfirmation returns false when stdin is not a TTY, and teamai remove had no flag to skip it. Every scripted run printed Cancelled and exited 0, which reads like success. The command could not be used from a script, and it had no end-to-end test at all. That is how both defects in #576 survived.

before                                   after
$ teamai remove rules doomed             $ teamai remove rules doomed --force
  Will remove 1 rules:                     Will remove 1 rules:
    - doomed                                 - doomed
  Cancelled            (exit 0)            ✔ Removed 1 rules from 2 location(s)
                                           ℹ Branch teamai/push/… has been pushed.

--force is spelled and described the same way as teamai uninstall --force, and the prompt keeps that command's guard shape so the two stay refactorable together. The command's action was also dropping its cmdOpts argument, so the option is wired through the way uninstall does it:

 .command('remove <type> <names...>')
+.option('--force', 'Skip confirmation prompt')
-.action(async (type, names) => {
+.action(async (type, names, cmdOpts) => {
   const globalOpts = program.opts() as GlobalOptions;
-  await remove(type, names, globalOpts);
+  await remove(type, names, { ...globalOpts, ...cmdOpts });
 });

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes
  • npx vitest run passes (233 files, 3265 tests)
  • Added/updated tests for the change

New end-to-end file, src/__tests__/e2e/remove-force.test.ts, driving the built CLI against a bare repository:

✓ removes without a prompt when --force is passed
✓ still cancels a non-interactive run without --force

The first test went red before the change on error: unknown option '--force'. The second is what makes the first meaningful. A non-interactive run without the flag still cancels and deletes nothing, so the deletions in test 1 can only come from --force.

The fixture gives the clone a real origin, so the run reaches pushRepoBranch instead of stopping at the local delete, and the test asserts the whole contract rather than a snapshot of the clone:

what assertion
exit code 0
deployed copy .claude/rules/doomed.md gone, keeper.md untouched
published branch matches teamai/push/, and on it rules/doomed.md is absent and rules/.removed contains doomed
clone working tree deliberately not asserted, see below

checkoutMaster returns the clone to the default branch after the push, so the clone's own working tree keeps the file until the branch merges. That is what "Remove a resource and open MR" means, and asserting otherwise would pin the wrong contract.

Each test builds its own sandbox. The e2e config retries once, and test 1 mutates the team repo, so a shared fixture would have the retry run against a used repository.

Related Issues

Fixes #591

Notes for Reviewers

What this unlocks beyond the flag itself. teamai remove had no end-to-end coverage, because no test could drive it. Both defects in #576 lived on this path and neither had a test at the command level. This PR adds the first one.

GlobalOptions.force carries two meanings, and this PR adds the second usage rather than the second meaning. pull --force ignores the unchanged-rev fast path; remove --force skips the prompt, as uninstall --force already does. The field's comment now says both. uninstall and init declare their own force on their own option types, so the comment describes only the two commands that read it from GlobalOptions.

Exit codes are unchanged and still uniform. Cancel, a failed push, an unsupported type, and "no matching resources" all exit 0. A script can now remove a resource but still cannot tell success from cancel. Changing that is a behaviour change beyond adding a flag, so it wants its own issue rather than a quiet ride here.

`askConfirmation` returns false when stdin is not a TTY, and `teamai
remove` had no flag to skip it, so every scripted run printed
"Cancelled" and exited 0. The command could not be used from a script,
and it had no end-to-end test, which is how both defects in Tencent#576
survived.

`--force` is spelled and described the same way as `teamai uninstall
--force`, and the prompt keeps the same guard shape so the two stay
refactorable together. The command's action was also dropping its
`cmdOpts` argument, so the option is wired through the way `uninstall`
does it.

The new end-to-end test drives the built CLI against a bare repository
and asserts the whole contract. The deployed copy goes immediately, and
the deletion plus its tombstone are published as a branch for review.
`checkoutMaster` returns the clone to the default branch, so the clone's
own working tree keeps the file until that branch merges.

Fixes Tencent#591
@SaulMoro
SaulMoro force-pushed the feat/remove-force-flag-591 branch from 04b5932 to 6b3fb50 Compare September 16, 2026 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] teamai remove cannot run unattended: no flag to skip the confirmation prompt

1 participant