shellfirm 0.3.7

`shellfirm` will intercept any risky patterns (default or defined by you) and prompt you a small challenge for double verification, kinda like a captcha for your terminal.
Documentation
---
# -- git:bisect --
- test: "git bisect start"
  description: "match bisect start"
  expect_ids: ["git:bisect"]
- test: "git bisect good"
  description: "match bisect good"
  expect_ids: ["git:bisect"]
- test: "git bisect bad"
  description: "match bisect bad"
  expect_ids: ["git:bisect"]
- test: "git bisect reset"
  description: "match bisect reset"
  expect_ids: ["git:bisect"]

# -- git:cherry_pick --
- test: "git cherry-pick abc123"
  description: "match cherry-pick command"
  expect_ids: ["git:cherry_pick"]
- test: "git cherry-pick --no-commit"
  description: "match cherry-pick without commit"
  expect_ids: ["git:cherry_pick"]

# -- git:clean_force --
- test: "git clean -fd"
  description: "match force clean command"
  expect_ids: ["git:clean_force"]
- test: "git clean -fdx"
  description: "match force clean command with x option"
  expect_ids: ["git:clean_force"]

# -- git:delete_all --
- test: "git  rm *"
  description: "match command"
  expect_ids: ["git:delete_all"]
- test: "git rm ."
  description: "match command"
  expect_ids: ["git:delete_all"]
- test: "git rm README.md"
  description: "negative: removing a specific file should not match"
  expect_ids: []
- test: "gitt rm"
  description: "invalid command"
  expect_ids: []

# -- git:delete_ref --
- test: "git update-ref -d refs/heads/feature"
  description: "match delete reference"
  expect_ids: ["git:delete_ref"]
- test: "git update-ref -d HEAD"
  description: "match delete HEAD reference"
  expect_ids: ["git:delete_ref"]

# -- git:filter_branch --
- test: "git filter-branch --env-filter"
  description: "match filter-branch command"
  expect_ids: ["git:filter_branch"]
- test: "git filter-branch --force"
  description: "match filter-branch with force"
  expect_ids: ["git:filter_branch"]

# -- git:force_checkout --
- test: "git checkout -f main"
  description: "match force checkout"
  expect_ids: ["git:force_checkout"]
- test: "git checkout -f"
  description: "match force checkout current branch"
  expect_ids: ["git:force_checkout"]

# -- git:force_delete_branch --
- test: "git branch -D feature"
  description: "match force delete branch"
  expect_ids: ["git:force_delete_branch"]
- test: "git branch -D"
  description: "match force delete current branch"
  expect_ids: ["git:force_delete_branch"]

# -- git:force_push --
- test: "git push -f"
  description: "match force push command"
  expect_ids: ["git:force_push"]
- test: "git push --force"
  description: "match force push with long option"
  expect_ids: ["git:force_push"]
- test: "git push origin --force"
  description: "match force push with remote before flag"
  expect_ids: ["git:force_push"]
- test: "git push origin main --force"
  description: "match force push with remote and branch before flag"
  expect_ids: ["git:force_push"]
- test: "git push origin -f"
  description: "match force push with remote before short flag"
  expect_ids: ["git:force_push"]
- test: "git push --force-with-lease"
  description: "force push with lease is the safe alternative and should not match"
  expect_ids: []

# -- git:gc_prune --
- test: "git gc --prune=now"
  description: "match garbage collection with prune"
  expect_ids: ["git:gc_prune"]
- test: "git   gc   --prune=now"
  description: "match garbage collection with prune and extra spaces"
  expect_ids: ["git:gc_prune"]
- test: "git gc --aggressive"
  description: "match aggressive garbage collection"
  expect_ids: []
- test: "git gc --prune"
  description: "should not match without =now"
  expect_ids: []

# -- git:interactive_rebase --
- test: "git rebase -i HEAD~3"
  description: "match interactive rebase"
  expect_ids: ["git-strict:rebase", "git:interactive_rebase"]
- test: "git rebase -i"
  description: "match interactive rebase without range"
  expect_ids: ["git-strict:rebase", "git:interactive_rebase"]

# -- git:merge_no_ff --
- test: "git merge --no-ff feature"
  description: "match non-fast-forward merge"
  expect_ids: ["git:merge_no_ff"]
- test: "git merge --abort"
  description: "match merge abort"
  expect_ids: ["git:merge_no_ff"]
- test: "git merge --no-ff"
  description: "match non-fast-forward merge without branch"
  expect_ids: ["git:merge_no_ff"]

# -- git:reset --
- test: "git reset"
  description: "match command"
  expect_ids: ["git:reset"]
- test: "git    reset"
  description: "match command"
  expect_ids: ["git:reset"]
- test: "gitt reset"
  description: "invalid command"
  expect_ids: []

# -- git:worktree_management --
- test: "git worktree add ../other"
  description: "match add worktree"
  expect_ids: ["git:worktree_management"]
- test: "git worktree remove ../other"
  description: "match remove worktree"
  expect_ids: ["git:worktree_management"]
- test: "git worktree add -f ../other"
  description: "match force add worktree"
  expect_ids: ["git:worktree_management"]
- test: "git worktree remove -f ../other"
  description: "match force remove worktree"
  expect_ids: ["git:worktree_management"]

# -- git-strict:add_all --
- test: "git add ."
  description: "match add all files"
  expect_ids: ["git-strict:add_all"]
- test: "git add -A"
  description: "match add all files with -A"
  expect_ids: ["git-strict:add_all"]
- test: "git add --all"
  description: "match add all files with --all"
  expect_ids: ["git-strict:add_all"]
- test: "git add README.md"
  description: "negative: adding a specific file should not match"
  expect_ids: []
- test: "git status"
  description: "negative: status command should not match add pattern"
  expect_ids: []

# -- git-strict:commit_all --
- test: "git commit --all"
  description: "match commit all changes"
  expect_ids: ["git-strict:commit_all"]
- test: "git commit -a"
  description: "match commit all with short option"
  expect_ids: ["git-strict:commit_all"]
- test: "git commit -am 'message'"
  description: "match commit all with message"
  expect_ids: ["git-strict:commit_all"]
- test: "git commit -m 'test'"
  description: "negative: commit with only -m (no -a) should not match"
  expect_ids: []
- test: "git commit --amend"
  description: "negative: amend should not match commit-all check"
  expect_ids: []
- test: "git commit --author='someone'"
  description: "negative: author flag should not match commit-all check"
  expect_ids: []

# -- git-strict:create_tag --
- test: "git tag -a v1.0.0"
  description: "match create annotated tag"
  expect_ids: ["git-strict:create_tag"]
- test: "git tag -a"
  description: "match create annotated tag without name"
  expect_ids: ["git-strict:create_tag"]
- test: "git tag v1.0.0"
  description: "negative: lightweight tag without -a should not match"
  expect_ids: []

# -- git-strict:rebase --
- test: "git rebase main"
  description: "match basic rebase"
  expect_ids: ["git-strict:rebase"]
- test: "git rebase"
  description: "match rebase without branch"
  expect_ids: ["git-strict:rebase"]
- test: "git pull --rebase main"
  description: "negative: pull with --rebase flag is not a rebase command"
  expect_ids: []

# -- git-strict:stash_pop_drop --
- test: "git stash pop"
  description: "match stash pop"
  expect_ids: ["git-strict:stash_pop_drop"]
- test: "git stash drop"
  description: "match stash drop"
  expect_ids: ["git-strict:stash_pop_drop"]
- test: "git stash list"
  description: "negative: listing stash entries should not match"
  expect_ids: []

# -- git-strict:submodule_update --
- test: "git submodule update"
  description: "match submodule update"
  expect_ids: ["git-strict:submodule_update"]
- test: "git submodule deinit"
  description: "match submodule deinit"
  expect_ids: ["git-strict:submodule_update"]
- test: "git submodule update --init"
  description: "match submodule update with init"
  expect_ids: ["git-strict:submodule_update"]
- test: "git submodule status"
  description: "negative: checking submodule status should not match"
  expect_ids: []

# -- git:clean_force — flag reordering --
- test: "git clean -df"
  description: "flag reorder -df now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -fxd"
  description: "flag reorder -fxd now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -xfd"
  description: "flag reorder -xfd now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -dfx"
  description: "flag reorder -dfx now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -xdf"
  description: "flag reorder -xdf now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -dxf"
  description: "flag reorder -dxf now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -f -d"
  description: "separate -f -d flags now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -d -f"
  description: "separate -d -f flags now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -f -d -x"
  description: "three separate flags now matched after regex fix"
  expect_ids: ["git:clean_force"]

- test: "git clean -fdX"
  description: "-fdX starts with -fd, matches"
  expect_ids: ["git:clean_force"]

- test: "git   clean   -fd"
  description: "extra spaces, still matches"
  expect_ids: ["git:clean_force"]

- test: "git clean -fdn"
  description: "-fdn starts with -fd, '-n' substring not present (n preceded by d not -)"
  expect_ids: ["git:clean_force"]

# -- git:force_delete_branch — long-form flags --
- test: "git branch --delete --force feature"
  description: "long-form --delete --force now matched after regex fix"
  expect_ids: ["git:force_delete_branch"]

- test: "git branch -d --force feature"
  description: "-d with --force now matched after regex fix"
  expect_ids: ["git:force_delete_branch"]

- test: "git branch -D feature/my-branch"
  description: "branch with slash in name"
  expect_ids: ["git:force_delete_branch"]

# -- git:force_checkout — long-form flag --
- test: "git checkout --force main"
  description: "long-form --force now matched after regex fix"
  expect_ids: ["git:force_checkout"]

- test: "git checkout -f -- ."
  description: "force checkout with -- separator"
  expect_ids: ["git:force_checkout"]

# -- git:reset — more variants --
- test: "git reset --hard"
  description: "hard reset, most dangerous variant"
  expect_ids: ["git:reset"]

- test: "git reset --hard HEAD~3"
  description: "hard reset to specific commit"
  expect_ids: ["git:reset"]

- test: "git reset --soft HEAD~1"
  description: "soft reset filtered out by NotContains"
  expect_ids: []

- test: "git reset --hard origin/main"
  description: "hard reset to remote"
  expect_ids: ["git:reset"]

# -- git:push_mirror --
- test: "git push --mirror origin"
  description: "match push --mirror with remote"
  expect_ids: ["git:push_mirror"]
- test: "git push origin --mirror"
  description: "match push with remote before --mirror"
  expect_ids: ["git:push_mirror"]
- test: "git push --mirror"
  description: "match push --mirror without remote"
  expect_ids: ["git:push_mirror"]
- test: "git push origin main"
  description: "negative: regular push should not match mirror"
  expect_ids: []

# -- git:push_delete_branch --
- test: "git push origin --delete feature-branch"
  description: "match push --delete branch"
  expect_ids: ["git:push_delete_branch"]
- test: "git push origin --delete main"
  description: "match push --delete main branch"
  expect_ids: ["git:push_delete_branch"]
- test: "git push origin :feature-branch"
  description: "match push colon syntax to delete remote branch"
  expect_ids: ["git:push_delete_branch"]
- test: "git push origin main"
  description: "negative: regular push should not match delete"
  expect_ids: []

# -- git:reflog_expire --
- test: "git reflog expire --expire=now --all"
  description: "match reflog expire now with --all"
  expect_ids: ["git:reflog_expire"]
- test: "git reflog expire --expire=now"
  description: "match reflog expire now without --all"
  expect_ids: ["git:reflog_expire"]
- test: "git reflog show"
  description: "negative: reflog show should not match"
  expect_ids: []
- test: "git reflog expire --expire=90.days"
  description: "negative: normal expire should not match"
  expect_ids: []