bash-classify 0.7.0__tar.gz → 0.8.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.
Files changed (201) hide show
  1. {bash_classify-0.7.0 → bash_classify-0.8.0}/.claude-plugin/marketplace.json +1 -1
  2. {bash_classify-0.7.0 → bash_classify-0.8.0}/PKG-INFO +1 -1
  3. {bash_classify-0.7.0 → bash_classify-0.8.0}/SPEC.md +4 -2
  4. {bash_classify-0.7.0 → bash_classify-0.8.0}/coding-agent-plugins/claude-code/.claude-plugin/plugin.json +1 -1
  5. {bash_classify-0.7.0 → bash_classify-0.8.0}/schemas/command.schema.json +8 -0
  6. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/bash.yaml +2 -0
  7. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sh.yaml +2 -0
  8. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/database.py +4 -0
  9. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/matcher.py +28 -8
  10. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/models.py +2 -0
  11. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_classifier.py +4 -2
  12. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_matcher.py +71 -0
  13. {bash_classify-0.7.0 → bash_classify-0.8.0}/.claude/rules/command-database.md +0 -0
  14. {bash_classify-0.7.0 → bash_classify-0.8.0}/.editorconfig +0 -0
  15. {bash_classify-0.7.0 → bash_classify-0.8.0}/.github/workflows/ci.yml +0 -0
  16. {bash_classify-0.7.0 → bash_classify-0.8.0}/.github/workflows/publish.yml +0 -0
  17. {bash_classify-0.7.0 → bash_classify-0.8.0}/.gitignore +0 -0
  18. {bash_classify-0.7.0 → bash_classify-0.8.0}/.python-version +0 -0
  19. {bash_classify-0.7.0 → bash_classify-0.8.0}/CLAUDE.md +0 -0
  20. {bash_classify-0.7.0 → bash_classify-0.8.0}/LICENSE +0 -0
  21. {bash_classify-0.7.0 → bash_classify-0.8.0}/README.md +0 -0
  22. {bash_classify-0.7.0 → bash_classify-0.8.0}/coding-agent-plugins/claude-code/hooks/hook-permission-request.sh +0 -0
  23. {bash_classify-0.7.0 → bash_classify-0.8.0}/coding-agent-plugins/claude-code/hooks/hook-pre-tool-use.sh +0 -0
  24. {bash_classify-0.7.0 → bash_classify-0.8.0}/coding-agent-plugins/claude-code/hooks/hooks.json +0 -0
  25. {bash_classify-0.7.0 → bash_classify-0.8.0}/docs/classification-guidance.md +0 -0
  26. {bash_classify-0.7.0 → bash_classify-0.8.0}/pyproject.toml +0 -0
  27. {bash_classify-0.7.0 → bash_classify-0.8.0}/ruff.toml +0 -0
  28. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/__init__.py +0 -0
  29. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/__main__.py +0 -0
  30. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/classifier.py +0 -0
  31. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/cli.py +0 -0
  32. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/agent-browser.yaml +0 -0
  33. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/apt.yaml +0 -0
  34. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/awk.yaml +0 -0
  35. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/basename.yaml +0 -0
  36. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/bash-classify.yaml +0 -0
  37. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/black.yaml +0 -0
  38. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/brew.yaml +0 -0
  39. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/builtin.yaml +0 -0
  40. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/cargo.yaml +0 -0
  41. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/cat.yaml +0 -0
  42. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/chgrp.yaml +0 -0
  43. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/chmod.yaml +0 -0
  44. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/chown.yaml +0 -0
  45. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/column.yaml +0 -0
  46. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/comm.yaml +0 -0
  47. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/command.yaml +0 -0
  48. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/cp.yaml +0 -0
  49. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/curl.yaml +0 -0
  50. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/cut.yaml +0 -0
  51. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/date.yaml +0 -0
  52. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/df.yaml +0 -0
  53. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/diff.yaml +0 -0
  54. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/dig.yaml +0 -0
  55. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/dirname.yaml +0 -0
  56. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/docker.yaml +0 -0
  57. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/du.yaml +0 -0
  58. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/echo.yaml +0 -0
  59. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/env.yaml +0 -0
  60. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/eslint.yaml +0 -0
  61. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/expand.yaml +0 -0
  62. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/false.yaml +0 -0
  63. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/file.yaml +0 -0
  64. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/find.yaml +0 -0
  65. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/fmt.yaml +0 -0
  66. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/fold.yaml +0 -0
  67. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/free.yaml +0 -0
  68. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gawk.yaml +0 -0
  69. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gcloud.yaml +0 -0
  70. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gh.yaml +0 -0
  71. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/git.yaml +0 -0
  72. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/glab-discussion.yaml +0 -0
  73. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/glab.yaml +0 -0
  74. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/go.yaml +0 -0
  75. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gog.yaml +0 -0
  76. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gradle.yaml +0 -0
  77. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gradlew.yaml +0 -0
  78. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/grep.yaml +0 -0
  79. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/groups.yaml +0 -0
  80. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gunzip.yaml +0 -0
  81. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gws.yaml +0 -0
  82. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/gzip.yaml +0 -0
  83. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/hadolint.yaml +0 -0
  84. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/head.yaml +0 -0
  85. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/helm.yaml +0 -0
  86. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/hostname.yaml +0 -0
  87. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/id.yaml +0 -0
  88. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/install.yaml +0 -0
  89. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/isort.yaml +0 -0
  90. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/java.yaml +0 -0
  91. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/javac.yaml +0 -0
  92. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/join.yaml +0 -0
  93. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/journalctl.yaml +0 -0
  94. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/jq.yaml +0 -0
  95. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/kill.yaml +0 -0
  96. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/killall.yaml +0 -0
  97. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/kubectl.yaml +0 -0
  98. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/less.yaml +0 -0
  99. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ln.yaml +0 -0
  100. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ls.yaml +0 -0
  101. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/make.yaml +0 -0
  102. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/md5sum.yaml +0 -0
  103. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mise.yaml +0 -0
  104. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mkdir.yaml +0 -0
  105. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/more.yaml +0 -0
  106. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mv.yaml +0 -0
  107. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mvn.yaml +0 -0
  108. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mvnw.yaml +0 -0
  109. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/mypy.yaml +0 -0
  110. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/netstat.yaml +0 -0
  111. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/nice.yaml +0 -0
  112. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/nl.yaml +0 -0
  113. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/node.yaml +0 -0
  114. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/nohup.yaml +0 -0
  115. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/npm.yaml +0 -0
  116. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/npx.yaml +0 -0
  117. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/nslookup.yaml +0 -0
  118. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/paste.yaml +0 -0
  119. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ping.yaml +0 -0
  120. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pip.yaml +0 -0
  121. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pip3.yaml +0 -0
  122. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pipx.yaml +0 -0
  123. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pkill.yaml +0 -0
  124. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pnpm.yaml +0 -0
  125. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/poetry.yaml +0 -0
  126. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/prettier.yaml +0 -0
  127. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/printenv.yaml +0 -0
  128. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/printf.yaml +0 -0
  129. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ps.yaml +0 -0
  130. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pup.yaml +0 -0
  131. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pwd.yaml +0 -0
  132. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/pytest.yaml +0 -0
  133. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/python.yaml +0 -0
  134. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/python3.yaml +0 -0
  135. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/read.yaml +0 -0
  136. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/readlink.yaml +0 -0
  137. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/realpath.yaml +0 -0
  138. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/rev.yaml +0 -0
  139. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/rg.yaml +0 -0
  140. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/rm.yaml +0 -0
  141. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/rsync.yaml +0 -0
  142. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ruff.yaml +0 -0
  143. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/scp.yaml +0 -0
  144. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sed.yaml +0 -0
  145. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/seq.yaml +0 -0
  146. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sha1sum.yaml +0 -0
  147. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sha256sum.yaml +0 -0
  148. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/shellcheck.yaml +0 -0
  149. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sort.yaml +0 -0
  150. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ss.yaml +0 -0
  151. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/ssh.yaml +0 -0
  152. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/stat.yaml +0 -0
  153. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/strace.yaml +0 -0
  154. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/sudo.yaml +0 -0
  155. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/systemctl.yaml +0 -0
  156. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tac.yaml +0 -0
  157. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tail.yaml +0 -0
  158. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tar.yaml +0 -0
  159. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tee.yaml +0 -0
  160. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/terraform.yaml +0 -0
  161. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/test.yaml +0 -0
  162. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/time.yaml +0 -0
  163. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/timeout.yaml +0 -0
  164. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/top.yaml +0 -0
  165. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/touch.yaml +0 -0
  166. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tr.yaml +0 -0
  167. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/traceroute.yaml +0 -0
  168. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/true.yaml +0 -0
  169. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/tsc.yaml +0 -0
  170. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/type.yaml +0 -0
  171. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/uname.yaml +0 -0
  172. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/unexpand.yaml +0 -0
  173. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/uniq.yaml +0 -0
  174. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/unzip.yaml +0 -0
  175. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/uptime.yaml +0 -0
  176. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/uv.yaml +0 -0
  177. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/wc.yaml +0 -0
  178. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/wget.yaml +0 -0
  179. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/whereis.yaml +0 -0
  180. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/which.yaml +0 -0
  181. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/whoami.yaml +0 -0
  182. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/xargs.yaml +0 -0
  183. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/xmllint.yaml +0 -0
  184. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/yarn.yaml +0 -0
  185. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/yes.yaml +0 -0
  186. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/yq.yaml +0 -0
  187. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/zip.yaml +0 -0
  188. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/commands/zsh.yaml +0 -0
  189. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/parser.py +0 -0
  190. {bash_classify-0.7.0 → bash_classify-0.8.0}/src/bash_classify/py.typed +0 -0
  191. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/__init__.py +0 -0
  192. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/conftest.py +0 -0
  193. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_cli.py +0 -0
  194. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_cli_serialization.py +0 -0
  195. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_database.py +0 -0
  196. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_models.py +0 -0
  197. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_parser.py +0 -0
  198. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_real_world.py +0 -0
  199. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_schema.py +0 -0
  200. {bash_classify-0.7.0 → bash_classify-0.8.0}/tests/test_smoke.py +0 -0
  201. {bash_classify-0.7.0 → bash_classify-0.8.0}/uv.lock +0 -0
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "bash-classify-hook",
10
10
  "description": "Auto-allows low-risk bash commands in Claude Code using bash-classify",
11
- "version": "0.7.0",
11
+ "version": "0.8.0",
12
12
  "author": {
13
13
  "name": "Filip Procházka",
14
14
  "url": "https://github.com/fprochazka"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bash-classify
3
- Version: 0.7.0
3
+ Version: 0.8.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
@@ -151,13 +151,13 @@ $ echo 'sh -c "ls /tmp | grep log"' | bash-classify
151
151
  ```json
152
152
  {
153
153
  "expression": "sh -c \"ls /tmp | grep log\"",
154
- "classification": "DANGEROUS",
154
+ "classification": "READONLY",
155
155
  "directories": ["/tmp"],
156
156
  "commands": [
157
157
  {
158
158
  "command": ["sh"],
159
159
  "argv": ["sh", "-c", "ls /tmp | grep log"],
160
- "classification": "DANGEROUS",
160
+ "classification": "READONLY",
161
161
  "matched_rule": "sh",
162
162
  "inner_commands": [
163
163
  {
@@ -850,6 +850,8 @@ The `delegates_to` field defines how a command (or option like `find -exec`) han
850
850
  | `flag` | `string` | For `flag_value_is_expression`: which flag's value to parse |
851
851
  | `strip_assignments` | `bool` | For `rest_are_argv`: strip leading `KEY=VALUE` tokens before the inner command |
852
852
  | `min_classification` | `enum` | Floor classification for the inner command (e.g. `sudo` forces at least `EXTERNAL_EFFECTS`) |
853
+ | `delegated_classification` | `enum` | When delegation resolves at least one inner command, the wrapper's own classification drops to this value before inner-elevation is applied. Intended for pure delegation wrappers like `sh -c` / `bash -c` where the wrapper itself has no effect beyond the command it runs. Only applies when the current classification came from the command's own base (not from an option override or strict-mode escalation). |
854
+ | `delegated_risk` | `enum` | Optional companion to `delegated_classification` — the risk floor applied when delegation resolves. Defaults to the default risk for `delegated_classification`. |
853
855
 
854
856
  ### Database field reference
855
857
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bash-classify-hook",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Auto-allows low-risk bash commands in Claude Code using bash-classify",
5
5
  "author": {
6
6
  "name": "Filip Prochazka"
@@ -116,6 +116,14 @@
116
116
  "min_classification": {
117
117
  "$ref": "#/$defs/Classification",
118
118
  "description": "Floor classification for the inner command"
119
+ },
120
+ "delegated_classification": {
121
+ "$ref": "#/$defs/Classification",
122
+ "description": "When delegation successfully resolves at least one inner command, the wrapper's own classification drops to this value before inner-elevation is applied. Use for pure delegation wrappers like sh/bash where the wrapper itself has no effect beyond the command it runs."
123
+ },
124
+ "delegated_risk": {
125
+ "$ref": "#/$defs/Risk",
126
+ "description": "Optional companion to delegated_classification — the risk floor applied when delegation resolves. Defaults to the default risk for delegated_classification."
119
127
  }
120
128
  }
121
129
  },
@@ -8,6 +8,8 @@ global_options:
8
8
  delegates_to:
9
9
  mode: flag_value_is_expression
10
10
  flag: -c
11
+ delegated_classification: READONLY
12
+ delegated_risk: LOW
11
13
  options:
12
14
  -c: {takes_value: true}
13
15
  -e: {}
@@ -7,6 +7,8 @@ global_options:
7
7
  delegates_to:
8
8
  mode: flag_value_is_expression
9
9
  flag: -c
10
+ delegated_classification: READONLY
11
+ delegated_risk: LOW
10
12
  options:
11
13
  -c: {takes_value: true}
12
14
  -e: {}
@@ -289,6 +289,8 @@ def _parse_delegation_config(raw: dict | None) -> DelegationConfig | None:
289
289
 
290
290
  mode = DelegationMode(raw["mode"])
291
291
  min_class = _parse_classification(raw.get("min_classification"))
292
+ delegated_class = _parse_classification(raw.get("delegated_classification"))
293
+ delegated_risk = _parse_risk(raw.get("delegated_risk"))
292
294
 
293
295
  return DelegationConfig(
294
296
  mode=mode,
@@ -297,4 +299,6 @@ def _parse_delegation_config(raw: dict | None) -> DelegationConfig | None:
297
299
  flag=raw.get("flag"),
298
300
  strip_assignments=raw.get("strip_assignments", False),
299
301
  min_classification=min_class,
302
+ delegated_classification=delegated_class,
303
+ delegated_risk=delegated_risk,
300
304
  )
@@ -248,24 +248,44 @@ def match_command(
248
248
 
249
249
  # Step 6: Handle delegation
250
250
  inner_commands: list[InnerCommandResult] = []
251
+ command_level_inner_count = 0
251
252
 
252
253
  # Command-level delegation
253
254
  if matched_def.delegates_to is not None:
254
- inner_commands.extend(
255
- _handle_delegation(
256
- remaining_positional,
257
- matched_def.delegates_to,
258
- database,
259
- argv,
260
- matched_def,
261
- )
255
+ command_level_inner = _handle_delegation(
256
+ remaining_positional,
257
+ matched_def.delegates_to,
258
+ database,
259
+ argv,
260
+ matched_def,
262
261
  )
262
+ command_level_inner_count = len(command_level_inner)
263
+ inner_commands.extend(command_level_inner)
263
264
 
264
265
  # Option-level delegation (e.g., find -exec)
265
266
  for opt_name, delegation_config, delegation_tokens in option_delegations:
266
267
  inner_results = _handle_option_delegation(opt_name, delegation_config, delegation_tokens, database)
267
268
  inner_commands.extend(inner_results)
268
269
 
270
+ # When command-level delegation actually produced inner commands, optionally
271
+ # drop the wrapper's own base classification to `delegated_classification`.
272
+ # This only applies when the current classification came from the command's
273
+ # own base (no option override and no strict-mode escalation) — otherwise
274
+ # we'd be erasing an intentional escalation.
275
+ if (
276
+ matched_def.delegates_to is not None
277
+ and matched_def.delegates_to.delegated_classification is not None
278
+ and command_level_inner_count > 0
279
+ and not all_overrides
280
+ and not (matched_def.strict and unknown_options)
281
+ ):
282
+ final_classification = matched_def.delegates_to.delegated_classification
283
+ if matched_def.delegates_to.delegated_risk is not None:
284
+ final_risk = matched_def.delegates_to.delegated_risk
285
+ else:
286
+ final_risk = _default_risk(final_classification)
287
+ classification_reason = f"delegated to inner via {matched_rule}"
288
+
269
289
  # Inner command classifications and risks affect the parent
270
290
  for inner in inner_commands:
271
291
  if inner.classification.severity() > final_classification.severity():
@@ -116,6 +116,8 @@ class DelegationConfig:
116
116
  flag: str | None = None
117
117
  strip_assignments: bool = False
118
118
  min_classification: Classification | None = None
119
+ delegated_classification: Classification | None = None
120
+ delegated_risk: Risk | None = None
119
121
 
120
122
 
121
123
  @dataclass
@@ -105,11 +105,13 @@ class TestSpecExamples:
105
105
  'sh -c "ls /tmp | grep log"',
106
106
  database=database,
107
107
  )
108
- assert result.classification == Classification.DANGEROUS
108
+ # sh -c with only READONLY inner commands drops the DANGEROUS floor
109
+ # via delegated_classification and ends up READONLY.
110
+ assert result.classification == Classification.READONLY
109
111
 
110
112
  sh_cmd = result.commands[0]
111
113
  assert sh_cmd.matched_rule == "sh"
112
- assert sh_cmd.classification == Classification.DANGEROUS
114
+ assert sh_cmd.classification == Classification.READONLY
113
115
  assert len(sh_cmd.inner_commands) >= 2
114
116
 
115
117
  inner_cmds = {tuple(ic.command) for ic in sh_cmd.inner_commands}
@@ -553,6 +553,77 @@ class TestShFlagEqualsValue:
553
553
  pytest.skip("sh -c=value form not supported — known limitation")
554
554
 
555
555
 
556
+ class TestDelegatedClassification:
557
+ """Tests for `delegated_classification` on delegates_to — sh/bash -c drops floor."""
558
+
559
+ def test_sh_c_readonly_inner(self, database: dict[str, CommandDef]) -> None:
560
+ """sh -c 'cat /tmp/x' -> READONLY/LOW (floor dropped, inner is READONLY)."""
561
+ result = match_command(_make_invocation(["sh", "-c", "cat /tmp/x"]), database)
562
+ assert result.classification == Classification.READONLY
563
+ assert result.risk == Risk.LOW
564
+
565
+ def test_bash_c_readonly_inner(self, database: dict[str, CommandDef]) -> None:
566
+ """bash -c 'cat /tmp/x' -> READONLY/LOW (same behavior for bash)."""
567
+ result = match_command(_make_invocation(["bash", "-c", "cat /tmp/x"]), database)
568
+ assert result.classification == Classification.READONLY
569
+ assert result.risk == Risk.LOW
570
+
571
+ def test_sh_c_dangerous_inner_elevates(self, database: dict[str, CommandDef]) -> None:
572
+ """sh -c 'rm -rf /' -> DANGEROUS (inner elevates past dropped floor)."""
573
+ result = match_command(_make_invocation(["sh", "-c", "rm -rf /"]), database)
574
+ assert result.classification == Classification.DANGEROUS
575
+ assert result.risk == Risk.HIGH
576
+
577
+ def test_sh_c_local_effects_inner(self, database: dict[str, CommandDef]) -> None:
578
+ """sh -c 'git add .' -> LOCAL_EFFECTS (inner elevates from READONLY floor)."""
579
+ result = match_command(_make_invocation(["sh", "-c", "git add ."]), database)
580
+ assert result.classification == Classification.LOCAL_EFFECTS
581
+ # git add is LOCAL_EFFECTS/LOW in the database
582
+ assert result.risk == Risk.LOW
583
+
584
+ def test_sh_without_c_preserves_base(self, database: dict[str, CommandDef]) -> None:
585
+ """sh alone (no -c) -> DANGEROUS/HIGH (no delegation fired, base preserved)."""
586
+ result = match_command(_make_invocation(["sh"]), database)
587
+ assert result.classification == Classification.DANGEROUS
588
+ assert result.risk == Risk.HIGH
589
+
590
+ def test_sh_c_empty_preserves_base(self, database: dict[str, CommandDef]) -> None:
591
+ """sh -c '' -> DANGEROUS (empty expression produces no inner, base preserved)."""
592
+ result = match_command(_make_invocation(["sh", "-c", ""]), database)
593
+ assert result.classification == Classification.DANGEROUS
594
+ assert result.risk == Risk.HIGH
595
+
596
+ def test_sudo_cat_unchanged(self, database: dict[str, CommandDef]) -> None:
597
+ """sudo cat x -> DANGEROUS (sudo.yaml untouched, regression test)."""
598
+ result = match_command(_make_invocation(["sudo", "cat", "/tmp/x"]), database)
599
+ assert result.classification == Classification.DANGEROUS
600
+
601
+ def test_sh_c_unknown_option_stays_dangerous(self, database: dict[str, CommandDef]) -> None:
602
+ """sh -c 'cat x' --bogus -> strict-mode escalation should not be dropped.
603
+
604
+ sh.yaml has default strict=true; an unknown option should escalate to
605
+ UNKNOWN/HIGH. The delegated_classification drop must not undo that.
606
+ """
607
+ result = match_command(_make_invocation(["sh", "--bogus", "-c", "cat /tmp/x"]), database)
608
+ # Inner is READONLY, but strict-mode unknown option should keep it non-READONLY.
609
+ assert result.classification != Classification.READONLY
610
+
611
+
612
+ class TestShXargsIntegration:
613
+ """Integration: xargs sh -c 'cat x' should come out as READONLY/LOW.
614
+
615
+ This uses parse_expression to exercise the full pipeline including xargs's
616
+ rest_are_argv delegation wrapping sh's flag_value_is_expression delegation.
617
+ """
618
+
619
+ def test_xargs_sh_c_readonly(self, database: dict[str, CommandDef]) -> None:
620
+ from bash_classify.classifier import classify_expression
621
+
622
+ result = classify_expression("xargs -I{} sh -c 'cat {}'")
623
+ assert result.classification == Classification.READONLY
624
+ assert result.risk == Risk.LOW
625
+
626
+
556
627
  class TestGlobalOptionOverrides:
557
628
  """Global options with overrides should affect classification."""
558
629
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes