bash-classify 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.claude-plugin/marketplace.json +1 -1
- {bash_classify-0.10.0 → bash_classify-0.11.0}/PKG-INFO +2 -1
- {bash_classify-0.10.0 → bash_classify-0.11.0}/README.md +1 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/SPEC.md +18 -2
- {bash_classify-0.10.0 → bash_classify-0.11.0}/coding-agent-plugins/claude-code/.claude-plugin/plugin.json +1 -1
- {bash_classify-0.10.0 → bash_classify-0.11.0}/docs/classification-guidance.md +19 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/schemas/command.schema.json +7 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/glab.yaml +2 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/database.py +50 -4
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/matcher.py +8 -3
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/models.py +8 -1
- bash_classify-0.11.0/tests/fixtures/match/glab-pipeline/block/pipe-get-alias.sh +1 -0
- bash_classify-0.11.0/tests/fixtures/match/glab-pipeline/block/pipeline-view-alias.sh +1 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_database.py +67 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_matcher.py +60 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_real_world.py +26 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.claude/rules/command-database.md +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.editorconfig +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.github/workflows/ci.yml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.github/workflows/publish.yml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.gitignore +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/.python-version +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/CLAUDE.md +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/LICENSE +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/coding-agent-plugins/claude-code/hooks/hook-permission-request.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/coding-agent-plugins/claude-code/hooks/hook-pre-tool-use.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/coding-agent-plugins/claude-code/hooks/hooks.json +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/pyproject.toml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/ruff.toml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/schemas/match-rules.schema.json +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/__init__.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/__main__.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/classifier.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/cli.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/agent-browser.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/apt.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/awk.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/basename.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/bash-classify.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/bash.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/black.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/brew.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/builtin.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/cargo.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/cat.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/chgrp.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/chmod.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/chown.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/column.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/comm.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/command.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/cp.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/curl.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/cut.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/date.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/df.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/diff.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/dig.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/dirname.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/docker.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/du.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/echo.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/env.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/eslint.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/eval.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/exec.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/expand.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/false.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/file.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/find.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/fmt.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/fold.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/free.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gawk.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gcloud.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gh.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/git.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/glab-discussion.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/go.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gog.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gradle.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gradlew.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/grep.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/groups.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gunzip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gws.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/gzip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/hadolint.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/head.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/helm.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/hostname.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/id.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/install.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/isort.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/java.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/javac.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/join.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/journalctl.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/jq.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/kill.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/killall.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/kubectl.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/less.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ln.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ls.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/make.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/md5sum.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mise.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mkdir.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/more.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mv.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mvn.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mvnw.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/mypy.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/netstat.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/nice.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/nl.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/node.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/nohup.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/npm.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/npx.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/nslookup.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/paste.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ping.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pip3.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pipx.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pkill.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pnpm.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/poetry.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/prettier.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/printenv.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/printf.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ps.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pup.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pwd.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/pytest.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/python.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/python3.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/read.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/readlink.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/realpath.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/rev.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/rg.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/rm.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/rsync.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/rtk.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ruff.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/scp.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sed.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/seq.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sh.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sha1sum.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sha256sum.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/shellcheck.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/slack.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sleep.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/snip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sort.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ss.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/ssh.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/stat.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/stdbuf.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/strace.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/strings.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/sudo.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/systemctl.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tac.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tail.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tar.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tee.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/terraform.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/test.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/time.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/timeout.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/top.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/touch.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tr.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/traceroute.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/true.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/tsc.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/type.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/uname.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/unexpand.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/uniq.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/unzip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/uptime.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/uv.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/wait.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/wc.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/wget.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/whereis.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/which.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/whoami.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/xargs.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/xmllint.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/yarn.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/yes.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/yq.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/zip.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/zsh.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/parser.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/py.typed +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/rules.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/__init__.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/conftest.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-merge-request-award-emoji.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-merge-request-include-diverged.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-merge-request-itself.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-merge-request-pipelines-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-merge-request-update-description.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/api-project-lookup.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/ci-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/comment-only-mention.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/echo-mentions-api-endpoint.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/echo-mentions-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/git-commit-heredoc-message.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/git-commit-inline-message.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/glab-discussion-read-and-api-mr.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/glab-discussion-read.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/glab-pipeline-inspect.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/grep-pattern-mentions-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/grep-recursive-for-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/heredoc-brief-2026-09-04.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/heredoc-brief-mentions-blocked-commands.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/heredoc-report-mentions-blocked.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/heredoc-skill-custom-delimiter.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/heredoc-skill-phrase-split-across-lines.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/hook-self-test-json-payload.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/loop-over-quoted-command-strings.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-note-list-output-json.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-note-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-update-then-view.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-view-bare.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-view-grep-pipeline.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-view-output-equals-json.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-view-output-json.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/mr-view-output-short-flag.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/printf-mentions-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/python-heredoc-triple-quoted-mention.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/allow/ripgrep-pattern-args.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-absolute-binary-path.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-delete-note-nested-api.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-cd-and-prefix.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-export-host-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-for-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-heredoc-then-call.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-hostname-python-consumer.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-line-continuation.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-method-get-field.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-output-ndjson.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-paginate-before-path.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-paginate.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-per-page-query.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-quoted-paginate-jq.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-redirect-to-file.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-repo-flag-first.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-discussions-var-capture.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-edit-note-put-heredoc.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-new-diff-note-input-stdin.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-new-diff-note-position-fields.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-note-award-emoji.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-note-by-id-or-retry.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-notes-after-allowed-mr-view.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-notes-query-sort.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-notes-timeout-prefix.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-reply-method-post-after-path.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-reply-post-body-from-file.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-reply-post-heredoc-body.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-resolve-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-resolve-put-field.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/api-single-discussion-by-id.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/heredoc-then-and-chain-to-blocked.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/heredoc-then-pipe-to-blocked.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-custom-heredoc-delimiter.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-help.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-heredoc-body.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-inline-after-mr-update.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-long-message-flag.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-repo-before-message.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-second-in-pipeline.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-note-then-close.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-bare.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-no-iid.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-page-flag.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-repo-flag.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-short-cluster.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/mr-view-comments-short-flag.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/wrapper-bash-c.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/block/wrapper-sudo.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-discussion/rules.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-job-metadata-without-trace.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-job-metadata-xargs.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-merge-request-itself.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-merge-request-pipelines-list-per-page.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-merge-request-pipelines-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-pipelines-list-bash-c.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-pipelines-list-ref.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/api-pipelines-list-single-quoted.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ci-lint-local-file.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ci-list-per-page.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ci-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ci-status-compact.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ci-status-pipeline-id.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/comment-only-mention.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/echo-mentions-ci-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/git-commit-heredoc-message.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/git-commit-inline-message.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/glab-discussion-read.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/glab-pipeline-inspect.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/grep-pattern-mentions-ci.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/heredoc-brief-mentions-ci-commands.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/heredoc-skill-mentions-ci.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/loop-over-quoted-command-strings.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/printf-mentions-ci-command.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/allow/ripgrep-pattern-args.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-ci-lint-continuation-lines.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-ci-lint-input-and-header.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-ci-lint-method-post-field.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-ci-lint-raw-field-cat.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-job-trace-after-allowed-metadata.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-job-trace-env-assignment.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-job-trace-redirect-to-file.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-job-trace-retry-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-job-trace-xargs.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-detail.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-env-assignment.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-hostname-after-path.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-comment-and-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-list.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-nested-loop-timeout.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-paginate-ndjson.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-python-consumer.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-jobs-scope-failed-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-status-poll-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/api-pipeline-test-report.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-after-allowed-status.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-branch-in-echo-chain.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-branch-ref-sleep-prefix.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-json-jq-sort-uniq.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-then-ci-trace.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-var-capture-poll-loop.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-get-with-job-details-repo.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-trace-job-name-with-pipeline.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-trace-pipeline-id-before-job-name.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-trace-redirect-to-file.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-trace-repo-before-job-id.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-view-help.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-view-job-web-or-api.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/ci-view-pipeline-id.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/heredoc-then-pipe-to-blocked.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/wrapper-bash-c.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/block/wrapper-sudo.sh +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/fixtures/match/glab-pipeline/rules.yaml +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_classifier.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_cli.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_cli_serialization.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_match_fixtures.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_models.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_parser.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_rules.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_schema.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/tests/test_smoke.py +0 -0
- {bash_classify-0.10.0 → bash_classify-0.11.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: bash-classify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Classify bash commands as READONLY, WRITE, DANGEROUS, or UNKNOWN
|
|
5
5
|
Project-URL: Homepage, https://github.com/fprochazka/bash-classify
|
|
6
6
|
Project-URL: Repository, https://github.com/fprochazka/bash-classify
|
|
@@ -180,6 +180,7 @@ Risk defaults are derived from classification (`READONLY`→LOW, `LOCAL_EFFECTS`
|
|
|
180
180
|
- **Tree-sitter parsing** -- bash expressions are parsed into an AST for accurate command extraction, handling pipes, subshells, and command substitution
|
|
181
181
|
- **YAML command database** -- each command has classification rules with subcommand and option matching
|
|
182
182
|
- **Subcommand matching** -- `kubectl get` and `kubectl delete` can have different classifications
|
|
183
|
+
- **Subcommand aliases** -- a subcommand can declare other names for itself, so `glab pipeline view` resolves to `glab ci view` and any rule written for the canonical name catches the aliased spelling
|
|
183
184
|
- **Multi-goal build tools** -- `subcommand_mode: match_all` handles commands like `mvn clean install` and `gradle clean build test` where multiple goals can be combined in any order
|
|
184
185
|
- **Delegation for wrappers** -- commands like `xargs`, `sudo`, and `env` delegate classification to the inner command
|
|
185
186
|
- **File path detection** -- redirect operators (`>`, `>>`, `<`) are parsed into `write_paths`/`read_paths` in the output; writes to `/tmp` and `/var/tmp` stay at LOW risk
|
|
@@ -151,6 +151,7 @@ Risk defaults are derived from classification (`READONLY`→LOW, `LOCAL_EFFECTS`
|
|
|
151
151
|
- **Tree-sitter parsing** -- bash expressions are parsed into an AST for accurate command extraction, handling pipes, subshells, and command substitution
|
|
152
152
|
- **YAML command database** -- each command has classification rules with subcommand and option matching
|
|
153
153
|
- **Subcommand matching** -- `kubectl get` and `kubectl delete` can have different classifications
|
|
154
|
+
- **Subcommand aliases** -- a subcommand can declare other names for itself, so `glab pipeline view` resolves to `glab ci view` and any rule written for the canonical name catches the aliased spelling
|
|
154
155
|
- **Multi-goal build tools** -- `subcommand_mode: match_all` handles commands like `mvn clean install` and `gradle clean build test` where multiple goals can be combined in any order
|
|
155
156
|
- **Delegation for wrappers** -- commands like `xargs`, `sudo`, and `env` delegate classification to the inner command
|
|
156
157
|
- **File path detection** -- redirect operators (`>`, `>>`, `<`) are parsed into `write_paths`/`read_paths` in the output; writes to `/tmp` and `/var/tmp` stay at LOW risk
|
|
@@ -398,7 +398,7 @@ This is the core of the tool. Given an `argv` like `["kubectl", "--context=prod"
|
|
|
398
398
|
|
|
399
399
|
1. **Look up the binary** (`kubectl`) in the database
|
|
400
400
|
2. **Separate global options** from the rest of `argv`, using the database definition of which options are global and whether they take a value
|
|
401
|
-
3. **Match the subcommand chain** from the remaining tokens (e.g. `get` → resolve to `kubectl.get`)
|
|
401
|
+
3. **Match the subcommand chain** from the remaining tokens (e.g. `get` → resolve to `kubectl.get`), resolving any declared subcommand alias to its canonical name
|
|
402
402
|
4. **Classify remaining options** against the matched subcommand's option definitions
|
|
403
403
|
5. **Determine the final classification** based on subcommand default + any option overrides
|
|
404
404
|
|
|
@@ -451,6 +451,14 @@ The default mode. Subcommands are matched as a greedy chain through nested subco
|
|
|
451
451
|
|
|
452
452
|
Example: `kubectl rollout status` matches `kubectl` → `rollout` → `status`, walking the tree.
|
|
453
453
|
|
|
454
|
+
#### Subcommand aliases
|
|
455
|
+
|
|
456
|
+
A subcommand may declare `aliases`. Every alias is another name for the same definition, so matching a word against a subcommand dictionary tries the canonical names and the aliases alike. What is reported is always the canonical name: `glab pipeline view 1000` yields `command: ["glab", "ci", "view"]` and `matched_rule: "glab.ci.view"`, while `argv` keeps `["glab", "pipeline", "view", "1000"]` — the word that was actually typed. Classification, options and `strict` behaviour are the canonical subcommand's, since it is literally the same definition.
|
|
457
|
+
|
|
458
|
+
This matters to `match` mode: a rule written as `command: [glab, ci, view]` catches the aliased spelling too, without listing it.
|
|
459
|
+
|
|
460
|
+
Aliases live in one namespace with their siblings. An alias that names a sibling subcommand, or that two siblings both claim, is rejected when the database file is loaded — the error names the file and the alias.
|
|
461
|
+
|
|
454
462
|
#### `match_all`
|
|
455
463
|
|
|
456
464
|
Each positional argument is matched independently against the **same** top-level subcommand dictionary. This is designed for build tools like Maven and Gradle where multiple goals/tasks appear as positional arguments in any order.
|
|
@@ -952,6 +960,14 @@ When command-level delegation resolves at least one inner command (and no option
|
|
|
952
960
|
| `subcommand_mode` | `enum` | How subcommands are matched: `hierarchical` (default, chained tree walk) or `match_all` (each positional arg matched independently) |
|
|
953
961
|
| `delegates_to` | `object` | How this command delegates execution to an inner command (see delegation modes) |
|
|
954
962
|
|
|
963
|
+
#### Subcommand level
|
|
964
|
+
|
|
965
|
+
A subcommand entry takes the same fields as the command level, minus `command` and `global_options` — its name is the key it is filed under — plus:
|
|
966
|
+
|
|
967
|
+
| Field | Type | Description |
|
|
968
|
+
|---|---|---|
|
|
969
|
+
| `aliases` | `list[str]` | Alternative names for this subcommand (e.g. `pipe` and `pipeline` for `glab ci`). Each alias resolves to this same definition; the resolved `command` path and `matched_rule` name the canonical subcommand, while `argv` keeps the word that was typed. An alias that collides with a sibling subcommand, or that two siblings both claim, is a load-time error. |
|
|
970
|
+
|
|
955
971
|
#### Option level
|
|
956
972
|
|
|
957
973
|
| Field | Type | Description |
|
|
@@ -1129,7 +1145,7 @@ builtins, an unknown binary, an empty argv — carry `null` internally and seria
|
|
|
1129
1145
|
- **Not a sandbox.** This is a classifier, not an enforcer. It does not execute or block anything. `match` mode is no exception: it reports which declared command shapes an expression invokes, and the caller decides what to do about it.
|
|
1130
1146
|
- **No awk/sed/perl script analysis.** These are classified as a whole command; their embedded programs are opaque. They should simply not be in the READONLY allowlist.
|
|
1131
1147
|
- **No variable resolution.** `$DIR`, `$(cmd)` in command position → UNKNOWN. We classify what we can see statically.
|
|
1132
|
-
- **No alias/function resolution.** We classify the literal command name as written.
|
|
1148
|
+
- **No shell alias/function resolution.** We classify the literal command name as written.
|
|
1133
1149
|
|
|
1134
1150
|
## Future extensions
|
|
1135
1151
|
|
|
@@ -84,6 +84,25 @@ options: # options that affect classification
|
|
|
84
84
|
| `subcommands` | map | -- | Nested subcommand definitions (recursive) |
|
|
85
85
|
| `delegates_to` | object | -- | How the command hands off to an inner command |
|
|
86
86
|
|
|
87
|
+
#### Subcommand fields
|
|
88
|
+
|
|
89
|
+
A subcommand takes the same fields as the top level, minus `command`, `description` and `global_options`, plus:
|
|
90
|
+
|
|
91
|
+
| Field | Type | Description |
|
|
92
|
+
|-------|------|-------------|
|
|
93
|
+
| `aliases` | list | Alternative names for this subcommand (e.g. `pipe` and `pipeline` for `glab ci`) |
|
|
94
|
+
|
|
95
|
+
An alias is another name for the same definition, not a copy of it: `glab pipeline view` classifies exactly as `glab ci view` and is reported as `["glab", "ci", "view"]`, while `argv` keeps the word that was typed. Declare aliases only for names the tool itself accepts (`glab ci --help` lists `pipe` and `pipeline`), and never a name a sibling subcommand already uses — that is rejected when the file loads.
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
subcommands:
|
|
99
|
+
ci:
|
|
100
|
+
aliases: [pipe, pipeline]
|
|
101
|
+
subcommands:
|
|
102
|
+
list:
|
|
103
|
+
classification: READONLY
|
|
104
|
+
```
|
|
105
|
+
|
|
87
106
|
#### Option fields
|
|
88
107
|
|
|
89
108
|
| Field | Type | Description |
|
|
@@ -174,6 +174,13 @@
|
|
|
174
174
|
"type": "object",
|
|
175
175
|
"additionalProperties": false,
|
|
176
176
|
"properties": {
|
|
177
|
+
"aliases": {
|
|
178
|
+
"type": "array",
|
|
179
|
+
"items": {
|
|
180
|
+
"type": "string"
|
|
181
|
+
},
|
|
182
|
+
"description": "Alternative names for this subcommand (e.g. `pipe` and `pipeline` for `glab ci`). A match through an alias resolves to this definition and reports the canonical name in the command path. An alias must not collide with a sibling subcommand or with another sibling's alias."
|
|
183
|
+
},
|
|
177
184
|
"classification": {
|
|
178
185
|
"$ref": "#/$defs/Classification"
|
|
179
186
|
},
|
|
@@ -181,8 +181,19 @@ _ALIAS_FORBIDDEN_KEYS = frozenset(
|
|
|
181
181
|
)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def _parse_command_def(data: dict, command_name: str) -> CommandDef:
|
|
185
|
-
"""Parse a raw YAML dict into a CommandDef structure.
|
|
184
|
+
def _parse_command_def(data: dict, command_name: str, is_subcommand: bool = False) -> CommandDef:
|
|
185
|
+
"""Parse a raw YAML dict into a CommandDef structure.
|
|
186
|
+
|
|
187
|
+
`is_subcommand` distinguishes an entry under a `subcommands` map from the top level of
|
|
188
|
+
a command file. Only the former may carry `aliases`; a command file names itself by its
|
|
189
|
+
filename, so a second name for it is a separate `alias_of` file.
|
|
190
|
+
"""
|
|
191
|
+
if "aliases" in data and not is_subcommand:
|
|
192
|
+
raise ValueError(
|
|
193
|
+
f"command '{command_name}': 'aliases' is only valid on subcommands; "
|
|
194
|
+
f"use an alias_of file for a command-level alias"
|
|
195
|
+
)
|
|
196
|
+
|
|
186
197
|
alias_of = data.get("alias_of")
|
|
187
198
|
if alias_of is not None:
|
|
188
199
|
conflicting = sorted(_ALIAS_FORBIDDEN_KEYS & data.keys())
|
|
@@ -205,6 +216,7 @@ def _parse_command_def(data: dict, command_name: str) -> CommandDef:
|
|
|
205
216
|
|
|
206
217
|
return CommandDef(
|
|
207
218
|
command=command_name,
|
|
219
|
+
aliases=_parse_aliases(data.get("aliases"), command_name),
|
|
208
220
|
classification=classification,
|
|
209
221
|
risk=risk,
|
|
210
222
|
global_options=global_options,
|
|
@@ -267,8 +279,23 @@ def _parse_options(raw: dict | None) -> dict[str, OptionDef]:
|
|
|
267
279
|
return options
|
|
268
280
|
|
|
269
281
|
|
|
282
|
+
def _parse_aliases(raw: object, command_name: str) -> list[str]:
|
|
283
|
+
"""Parse the `aliases` list of a subcommand definition."""
|
|
284
|
+
if raw is None:
|
|
285
|
+
return []
|
|
286
|
+
if not isinstance(raw, list):
|
|
287
|
+
raise ValueError(f"subcommand '{command_name}': 'aliases' must be a list of strings")
|
|
288
|
+
return [_yaml_str(alias) for alias in raw]
|
|
289
|
+
|
|
290
|
+
|
|
270
291
|
def _parse_subcommands(raw: dict | None) -> dict[str, CommandDef]:
|
|
271
|
-
"""Parse a subcommands map recursively.
|
|
292
|
+
"""Parse a subcommands map recursively.
|
|
293
|
+
|
|
294
|
+
Each alias is registered in the same map as the definition it belongs to, pointing at
|
|
295
|
+
that very object, so `_match_subcommand` finds it by the typed word and still reports
|
|
296
|
+
the canonical name. An alias that would shadow a real sibling subcommand, or that two
|
|
297
|
+
siblings claim, is rejected here rather than silently resolving to one of them.
|
|
298
|
+
"""
|
|
272
299
|
if not raw:
|
|
273
300
|
return {}
|
|
274
301
|
|
|
@@ -277,7 +304,26 @@ def _parse_subcommands(raw: dict | None) -> dict[str, CommandDef]:
|
|
|
277
304
|
for name, props in raw.items():
|
|
278
305
|
if props is None:
|
|
279
306
|
props = {}
|
|
280
|
-
|
|
307
|
+
canonical = _yaml_str(name)
|
|
308
|
+
subcommands[canonical] = _parse_command_def(props, canonical, is_subcommand=True)
|
|
309
|
+
|
|
310
|
+
aliased_by: dict[str, str] = {}
|
|
311
|
+
for canonical, definition in list(subcommands.items()):
|
|
312
|
+
for alias in definition.aliases:
|
|
313
|
+
if alias in subcommands:
|
|
314
|
+
raise ValueError(
|
|
315
|
+
f"subcommand '{canonical}' declares alias '{alias}', "
|
|
316
|
+
f"which is already a subcommand of the same parent"
|
|
317
|
+
)
|
|
318
|
+
if alias in aliased_by:
|
|
319
|
+
raise ValueError(
|
|
320
|
+
f"subcommand '{canonical}' declares alias '{alias}', "
|
|
321
|
+
f"which is already an alias of subcommand '{aliased_by[alias]}'"
|
|
322
|
+
)
|
|
323
|
+
aliased_by[alias] = canonical
|
|
324
|
+
|
|
325
|
+
for alias, canonical in aliased_by.items():
|
|
326
|
+
subcommands[alias] = subcommands[canonical]
|
|
281
327
|
|
|
282
328
|
return subcommands
|
|
283
329
|
|
|
@@ -452,6 +452,10 @@ def _match_subcommand(
|
|
|
452
452
|
) -> tuple[CommandDef, list[str], list[str]]:
|
|
453
453
|
"""Match subcommand chain from argv.
|
|
454
454
|
|
|
455
|
+
The chain holds canonical subcommand names: a word typed as a declared alias
|
|
456
|
+
(`glab pipeline view` for `glab ci view`) resolves to the definition it names, and it
|
|
457
|
+
is that definition's own name that lands in the chain. `argv` keeps the typed word.
|
|
458
|
+
|
|
455
459
|
Returns (matched_def, command_chain, remaining_argv).
|
|
456
460
|
"""
|
|
457
461
|
matched_def = command_def
|
|
@@ -466,8 +470,8 @@ def _match_subcommand(
|
|
|
466
470
|
break
|
|
467
471
|
|
|
468
472
|
if token in matched_def.subcommands:
|
|
469
|
-
command_chain.append(token)
|
|
470
473
|
matched_def = matched_def.subcommands[token]
|
|
474
|
+
command_chain.append(matched_def.command)
|
|
471
475
|
remaining = remaining[1:]
|
|
472
476
|
else:
|
|
473
477
|
break
|
|
@@ -496,8 +500,9 @@ def _match_all_subcommands(
|
|
|
496
500
|
# Options pass through for _classify_options later
|
|
497
501
|
remaining.append(token)
|
|
498
502
|
elif token in command_def.subcommands:
|
|
499
|
-
|
|
500
|
-
|
|
503
|
+
matched = command_def.subcommands[token]
|
|
504
|
+
matched_names.append(matched.command)
|
|
505
|
+
matched_defs.append(matched)
|
|
501
506
|
else:
|
|
502
507
|
# Unrecognized goal — pass through as positional
|
|
503
508
|
remaining.append(token)
|
|
@@ -134,10 +134,17 @@ class OptionDef:
|
|
|
134
134
|
|
|
135
135
|
@dataclass
|
|
136
136
|
class CommandDef:
|
|
137
|
-
"""Definition of a command (or subcommand) from the database.
|
|
137
|
+
"""Definition of a command (or subcommand) from the database.
|
|
138
|
+
|
|
139
|
+
``command`` is the canonical name. A subcommand may additionally be reachable under
|
|
140
|
+
the names in ``aliases``: the parent's ``subcommands`` map holds the very same
|
|
141
|
+
definition object under each of them, so a matched alias still reports the canonical
|
|
142
|
+
name in the resolved command path.
|
|
143
|
+
"""
|
|
138
144
|
|
|
139
145
|
command: str
|
|
140
146
|
alias_of: str | None = None
|
|
147
|
+
aliases: list[str] = field(default_factory=list)
|
|
141
148
|
classification: Classification | None = None
|
|
142
149
|
risk: Risk | None = None
|
|
143
150
|
global_options: dict[str, OptionDef] = field(default_factory=dict)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
glab pipe get -p 1000 --with-job-details -F json 2>/dev/null | jq -r '.jobs[] | "\(.status)\t\(.name)"'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
glab pipeline view 1000 2>&1 | head -50
|
|
@@ -238,6 +238,73 @@ class TestAliasOf:
|
|
|
238
238
|
assert db["myalias"].alias_of == "target"
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
class TestSubcommandAliases:
|
|
242
|
+
def test_alias_maps_to_the_same_definition_object(self, database: dict[str, CommandDef]) -> None:
|
|
243
|
+
"""`glab pipe` and `glab pipeline` are glab's own deprecated names for `glab ci`."""
|
|
244
|
+
glab = database["glab"]
|
|
245
|
+
ci = glab.subcommands["ci"]
|
|
246
|
+
assert glab.subcommands["pipe"] is ci
|
|
247
|
+
assert glab.subcommands["pipeline"] is ci
|
|
248
|
+
|
|
249
|
+
def test_alias_keeps_the_canonical_name(self, database: dict[str, CommandDef]) -> None:
|
|
250
|
+
glab = database["glab"]
|
|
251
|
+
assert glab.subcommands["pipeline"].command == "ci"
|
|
252
|
+
assert glab.subcommands["ci"].aliases == ["pipe", "pipeline"]
|
|
253
|
+
|
|
254
|
+
def test_subcommand_without_aliases_has_an_empty_list(self, database: dict[str, CommandDef]) -> None:
|
|
255
|
+
assert database["glab"].subcommands["mr"].aliases == []
|
|
256
|
+
|
|
257
|
+
def test_nested_alias_is_registered(self, tmp_path: Path) -> None:
|
|
258
|
+
"""Aliases work at any depth, not only on the first level."""
|
|
259
|
+
(tmp_path / "tool.yaml").write_text(
|
|
260
|
+
"command: tool\n"
|
|
261
|
+
"subcommands:\n"
|
|
262
|
+
" remote:\n"
|
|
263
|
+
" subcommands:\n"
|
|
264
|
+
" list:\n"
|
|
265
|
+
" aliases: [ls]\n"
|
|
266
|
+
" classification: READONLY\n"
|
|
267
|
+
)
|
|
268
|
+
db = load_database(tmp_path)
|
|
269
|
+
remote = db["tool"].subcommands["remote"]
|
|
270
|
+
assert remote.subcommands["ls"] is remote.subcommands["list"]
|
|
271
|
+
|
|
272
|
+
def test_alias_colliding_with_a_sibling_subcommand_is_rejected(self, tmp_path: Path) -> None:
|
|
273
|
+
(tmp_path / "tool.yaml").write_text(
|
|
274
|
+
"command: tool\nsubcommands:\n ci:\n aliases: [status]\n status:\n classification: READONLY\n"
|
|
275
|
+
)
|
|
276
|
+
db = load_database(tmp_path)
|
|
277
|
+
with pytest.raises(ValueError, match="tool.yaml.*alias 'status'.*already a subcommand"):
|
|
278
|
+
db["tool"]
|
|
279
|
+
|
|
280
|
+
def test_alias_claimed_by_two_siblings_is_rejected(self, tmp_path: Path) -> None:
|
|
281
|
+
(tmp_path / "tool.yaml").write_text(
|
|
282
|
+
"command: tool\nsubcommands:\n ci:\n aliases: [p]\n publish:\n aliases: [p]\n"
|
|
283
|
+
)
|
|
284
|
+
db = load_database(tmp_path)
|
|
285
|
+
with pytest.raises(ValueError, match="tool.yaml.*alias 'p'.*already an alias of subcommand 'ci'"):
|
|
286
|
+
db["tool"]
|
|
287
|
+
|
|
288
|
+
def test_alias_repeating_its_own_subcommand_name_is_rejected(self, tmp_path: Path) -> None:
|
|
289
|
+
(tmp_path / "tool.yaml").write_text("command: tool\nsubcommands:\n ci:\n aliases: [ci]\n")
|
|
290
|
+
db = load_database(tmp_path)
|
|
291
|
+
with pytest.raises(ValueError, match="tool.yaml.*alias 'ci'.*already a subcommand"):
|
|
292
|
+
db["tool"]
|
|
293
|
+
|
|
294
|
+
def test_top_level_aliases_key_is_rejected(self, tmp_path: Path) -> None:
|
|
295
|
+
"""A command file names itself by its filename; a second name is an alias_of file."""
|
|
296
|
+
(tmp_path / "tool.yaml").write_text("command: tool\naliases: [t]\nclassification: READONLY\n")
|
|
297
|
+
db = load_database(tmp_path)
|
|
298
|
+
with pytest.raises(ValueError, match="tool.yaml.*'aliases' is only valid on subcommands"):
|
|
299
|
+
db["tool"]
|
|
300
|
+
|
|
301
|
+
def test_aliases_must_be_a_list(self, tmp_path: Path) -> None:
|
|
302
|
+
(tmp_path / "tool.yaml").write_text("command: tool\nsubcommands:\n ci:\n aliases: pipe\n")
|
|
303
|
+
db = load_database(tmp_path)
|
|
304
|
+
with pytest.raises(ValueError, match="tool.yaml.*'aliases' must be a list"):
|
|
305
|
+
db["tool"]
|
|
306
|
+
|
|
307
|
+
|
|
241
308
|
class TestUserCommandsDir:
|
|
242
309
|
def test_user_override_replaces_builtin(self, tmp_path: Path) -> None:
|
|
243
310
|
"""User YAML overrides built-in command definition."""
|
|
@@ -837,6 +837,66 @@ class TestRiskOptionOverrides:
|
|
|
837
837
|
assert result.risk == Risk.HIGH
|
|
838
838
|
|
|
839
839
|
|
|
840
|
+
class TestSubcommandAliases:
|
|
841
|
+
"""A subcommand reached through an alias reports the canonical name.
|
|
842
|
+
|
|
843
|
+
`command` and `matched_rule` are what a rule or an allowlist is written against, so
|
|
844
|
+
they must name the subcommand the database defines. `argv` is what the user typed and
|
|
845
|
+
stays untouched.
|
|
846
|
+
"""
|
|
847
|
+
|
|
848
|
+
def test_alias_resolves_to_the_canonical_command_path(self, database: dict[str, CommandDef]) -> None:
|
|
849
|
+
result = match_command(_make_invocation(["glab", "pipeline", "view", "1000"]), database)
|
|
850
|
+
assert result.command == ["glab", "ci", "view"]
|
|
851
|
+
assert result.argv == ["glab", "pipeline", "view", "1000"]
|
|
852
|
+
assert result.matched_rule == "glab.ci.view"
|
|
853
|
+
|
|
854
|
+
def test_alias_classifies_exactly_as_the_canonical_subcommand(self, database: dict[str, CommandDef]) -> None:
|
|
855
|
+
typed = match_command(_make_invocation(["glab", "pipe", "get", "-p", "1000", "-F", "json"]), database)
|
|
856
|
+
canonical = match_command(_make_invocation(["glab", "ci", "get", "-p", "1000", "-F", "json"]), database)
|
|
857
|
+
assert typed.command == canonical.command
|
|
858
|
+
assert typed.classification == canonical.classification
|
|
859
|
+
assert typed.risk == canonical.risk
|
|
860
|
+
assert typed.options == canonical.options
|
|
861
|
+
assert typed.positionals == canonical.positionals
|
|
862
|
+
|
|
863
|
+
def test_alias_only_shadows_at_its_own_level(self, database: dict[str, CommandDef]) -> None:
|
|
864
|
+
"""`pipeline` is an alias of `glab ci`, not a word that means `ci` everywhere."""
|
|
865
|
+
result = match_command(_make_invocation(["glab", "mr", "pipeline"]), database)
|
|
866
|
+
assert result.command == ["glab", "mr"]
|
|
867
|
+
|
|
868
|
+
def test_nested_alias_reports_the_canonical_path(self) -> None:
|
|
869
|
+
inner = CommandDef(command="list", aliases=["ls"], classification=Classification.READONLY)
|
|
870
|
+
outer = CommandDef(command="remote", subcommands={"list": inner, "ls": inner})
|
|
871
|
+
database = {"tool": CommandDef(command="tool", subcommands={"remote": outer})}
|
|
872
|
+
result = match_command(_make_invocation(["tool", "remote", "ls", "origin"]), database)
|
|
873
|
+
assert result.command == ["tool", "remote", "list"]
|
|
874
|
+
assert result.argv == ["tool", "remote", "ls", "origin"]
|
|
875
|
+
assert result.matched_rule == "tool.remote.list"
|
|
876
|
+
assert result.classification == Classification.READONLY
|
|
877
|
+
|
|
878
|
+
def test_alias_inherits_strict_mode(self) -> None:
|
|
879
|
+
"""The alias is the same definition, so an unknown option is UNKNOWN either way."""
|
|
880
|
+
sub = CommandDef(command="build", aliases=["b"], classification=Classification.LOCAL_EFFECTS, strict=True)
|
|
881
|
+
database = {"tool": CommandDef(command="tool", subcommands={"build": sub, "b": sub})}
|
|
882
|
+
result = match_command(_make_invocation(["tool", "b", "--no-such-flag"]), database)
|
|
883
|
+
assert result.command == ["tool", "build"]
|
|
884
|
+
assert result.classification == Classification.UNKNOWN
|
|
885
|
+
|
|
886
|
+
def test_alias_in_match_all_mode_reports_the_canonical_goal(self) -> None:
|
|
887
|
+
clean = CommandDef(command="clean", aliases=["clobber"], classification=Classification.LOCAL_EFFECTS)
|
|
888
|
+
database = {
|
|
889
|
+
"build-tool": CommandDef(
|
|
890
|
+
command="build-tool",
|
|
891
|
+
subcommand_mode=SubcommandMode.MATCH_ALL,
|
|
892
|
+
subcommands={"clean": clean, "clobber": clean},
|
|
893
|
+
)
|
|
894
|
+
}
|
|
895
|
+
result = match_command(_make_invocation(["build-tool", "clobber"]), database)
|
|
896
|
+
assert result.command == ["build-tool", "clean"]
|
|
897
|
+
assert result.classification == Classification.LOCAL_EFFECTS
|
|
898
|
+
|
|
899
|
+
|
|
840
900
|
class TestRiskExplicitOverrideInCommandDef:
|
|
841
901
|
def test_explicit_risk_in_command_def(self) -> None:
|
|
842
902
|
"""A command with explicit risk: LOW should use that risk."""
|
|
@@ -599,6 +599,32 @@ class TestGlabOptions:
|
|
|
599
599
|
assert command.options == ["--per-page"]
|
|
600
600
|
assert command.positionals == []
|
|
601
601
|
|
|
602
|
+
def test_ci_pipeline_alias(self, database):
|
|
603
|
+
"""`glab pipeline` is glab's own deprecated spelling of `glab ci`."""
|
|
604
|
+
result = classify_expression("glab pipeline view 1000 --web", database)
|
|
605
|
+
assert result.classification == Classification.READONLY
|
|
606
|
+
command = result.commands[0]
|
|
607
|
+
assert command.command == ["glab", "ci", "view"]
|
|
608
|
+
assert command.argv == ["glab", "pipeline", "view", "1000", "--web"]
|
|
609
|
+
assert command.matched_rule == "glab.ci.view"
|
|
610
|
+
assert command.options == ["--web"]
|
|
611
|
+
assert command.positionals == ["1000"]
|
|
612
|
+
|
|
613
|
+
def test_ci_pipe_alias(self, database):
|
|
614
|
+
result = classify_expression("glab pipe get -p 1000 --with-job-details -F json", database)
|
|
615
|
+
assert result.classification == Classification.READONLY
|
|
616
|
+
command = result.commands[0]
|
|
617
|
+
assert command.command == ["glab", "ci", "get"]
|
|
618
|
+
assert command.argv[1] == "pipe"
|
|
619
|
+
assert command.options == ["-p", "--with-job-details", "-F"]
|
|
620
|
+
|
|
621
|
+
def test_ci_alias_inside_a_wrapper(self, database):
|
|
622
|
+
"""The alias resolves the same way in a delegated inner command."""
|
|
623
|
+
result = classify_expression("timeout 60 glab pipeline trace deploy-test -p 1000", database)
|
|
624
|
+
inner = result.commands[0].inner_commands[0]
|
|
625
|
+
assert inner.command == ["glab", "ci", "trace"]
|
|
626
|
+
assert inner.argv == ["glab", "pipeline", "trace", "deploy-test", "-p", "1000"]
|
|
627
|
+
|
|
602
628
|
def test_glab_classifications_unchanged(self, database):
|
|
603
629
|
"""Declaring options must not move any classification or risk."""
|
|
604
630
|
assert classify_expression("glab mr view 42", database).classification == Classification.READONLY
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bash_classify-0.10.0 → bash_classify-0.11.0}/coding-agent-plugins/claude-code/hooks/hooks.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bash_classify-0.10.0 → bash_classify-0.11.0}/src/bash_classify/commands/glab-discussion.yaml
RENAMED
|
File without changes
|