gitwise-cli 0.29.0__tar.gz → 0.31.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 (178) hide show
  1. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/CHANGELOG.md +12 -0
  2. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/PKG-INFO +1 -1
  3. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/__init__.py +4 -1
  4. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/__main__.py +4 -0
  5. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_cli_completions.py +4 -0
  6. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_cli_dispatch.py +91 -0
  7. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_cli_introspection.py +14 -0
  8. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_cli_parser.py +29 -0
  9. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_cli_setup_agents.py +5 -0
  10. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_i18n_data.json +44 -0
  11. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_runtime_config.py +17 -0
  12. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/audit.py +11 -0
  13. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/branches.py +10 -0
  14. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/clean.py +6 -0
  15. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/commit.py +84 -1
  16. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/conflicts.py +8 -0
  17. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/context.py +9 -0
  18. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/design.py +26 -0
  19. gitwise_cli-0.31.0/gitwise/diff.py +544 -0
  20. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/doctor.py +5 -0
  21. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/git.py +40 -0
  22. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/health.py +25 -0
  23. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/i18n.py +13 -0
  24. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/log.py +20 -0
  25. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/merge.py +10 -0
  26. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/optimize.py +12 -0
  27. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/output.py +45 -0
  28. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/pick.py +6 -0
  29. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/pr.py +31 -0
  30. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/schema.py +5 -0
  31. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup.py +28 -0
  32. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/exec.py +31 -3
  33. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/format.py +8 -0
  34. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/plan.py +17 -0
  35. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/plan_gitfiles.py +11 -0
  36. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/plan_skills.py +10 -0
  37. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/__init__.py +9 -0
  38. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/base.py +9 -0
  39. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/claude.py +18 -0
  40. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/state.py +7 -0
  41. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/types.py +5 -0
  42. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/show.py +9 -0
  43. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/snapshot.py +6 -0
  44. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/stash.py +8 -0
  45. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/status.py +1 -0
  46. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/suggest.py +10 -0
  47. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/summarize.py +5 -0
  48. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/sync.py +13 -0
  49. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/tag.py +11 -0
  50. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/undo.py +9 -0
  51. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/update.py +1 -0
  52. gitwise_cli-0.31.0/gitwise/utils/secret_scan.py +201 -0
  53. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/worktree.py +5 -0
  54. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/pyproject.toml +1 -1
  55. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/commit.json +5 -0
  56. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/diff.json +21 -0
  57. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_commit.py +58 -0
  58. gitwise_cli-0.31.0/tests/test_diff.py +351 -0
  59. gitwise_cli-0.31.0/tests/test_secret_scan.py +177 -0
  60. gitwise_cli-0.29.0/gitwise/diff.py +0 -321
  61. gitwise_cli-0.29.0/tests/test_diff.py +0 -168
  62. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/.gitignore +0 -0
  63. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/LICENSE +0 -0
  64. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/README.md +0 -0
  65. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/bin/gitwise +0 -0
  66. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/_paths.py +0 -0
  67. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/py.typed +0 -0
  68. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/__init__.py +0 -0
  69. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/__init__.py +0 -0
  70. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/aider.py +0 -0
  71. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/base.py +0 -0
  72. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/codex.py +0 -0
  73. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/continue_adapter.py +0 -0
  74. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/cursor.py +0 -0
  75. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/opencode.py +0 -0
  76. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/adapters/pi.py +0 -0
  77. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/aider.py +0 -0
  78. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/codex.py +0 -0
  79. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/continue_adapter.py +0 -0
  80. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/cursor.py +0 -0
  81. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/opencode.py +0 -0
  82. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/setup_agents/providers/pi.py +0 -0
  83. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/utils/__init__.py +0 -0
  84. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/utils/git_output.py +0 -0
  85. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/utils/in_progress.py +0 -0
  86. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/utils/json_envelope.py +0 -0
  87. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/gitwise/utils/parsing.py +0 -0
  88. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/install.sh +0 -0
  89. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/agents/skills/git-audit/SKILL.md +0 -0
  90. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/agents/skills/git-clean/SKILL.md +0 -0
  91. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/agents/skills/git-optimize/SKILL.md +0 -0
  92. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/aider/CONVENTIONS.md.template +0 -0
  93. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/aider/aider.conf.yml.template +0 -0
  94. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/CLAUDE.md.template +0 -0
  95. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/rules/gitwise.md +0 -0
  96. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/settings.json.template +0 -0
  97. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/skills/git-audit/SKILL.md +0 -0
  98. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/skills/git-clean/SKILL.md +0 -0
  99. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/claude/skills/git-optimize/SKILL.md +0 -0
  100. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/codex/agents/gitwise.toml.template +0 -0
  101. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/continue/rules/gitwise.md.template +0 -0
  102. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/cursor/rules/gitwise.mdc.template +0 -0
  103. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/git-config-modern.txt +0 -0
  104. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/hooks/commit-msg +0 -0
  105. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/hooks/pre-commit +0 -0
  106. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/opencode/agents/gitwise.md.template +0 -0
  107. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/pi/skills/gitwise.md.template +0 -0
  108. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/audit.json +0 -0
  109. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/branches.json +0 -0
  110. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/clean.json +0 -0
  111. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/commands.json +0 -0
  112. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/completions.json +0 -0
  113. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/conflicts.json +0 -0
  114. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/context.json +0 -0
  115. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/doctor.json +0 -0
  116. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/health.json +0 -0
  117. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/log.json +0 -0
  118. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/merge.json +0 -0
  119. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/optimize.json +0 -0
  120. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/pick.json +0 -0
  121. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/pr.json +0 -0
  122. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/schema.json +0 -0
  123. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/setup-agents.json +0 -0
  124. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/setup.json +0 -0
  125. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/show.json +0 -0
  126. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/snapshot.json +0 -0
  127. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/stash.json +0 -0
  128. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/status.json +0 -0
  129. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/suggest.json +0 -0
  130. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/summarize.json +0 -0
  131. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/sync.json +0 -0
  132. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/tag.json +0 -0
  133. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/undo.json +0 -0
  134. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/update.json +0 -0
  135. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/share/schemas/v1/input/worktree.json +0 -0
  136. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/__init__.py +0 -0
  137. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/conftest.py +0 -0
  138. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_adapters.py +0 -0
  139. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_audit.py +0 -0
  140. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_branches.py +0 -0
  141. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_clean.py +0 -0
  142. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_conflicts.py +0 -0
  143. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_context.py +0 -0
  144. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_design.py +0 -0
  145. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_doctor.py +0 -0
  146. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_edge_cases.py +0 -0
  147. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_git.py +0 -0
  148. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_git_output_utils.py +0 -0
  149. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_health.py +0 -0
  150. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_i18n.py +0 -0
  151. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_in_progress.py +0 -0
  152. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_json_envelope_utils.py +0 -0
  153. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_log.py +0 -0
  154. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_main.py +0 -0
  155. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_md_links_script.py +0 -0
  156. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_merge.py +0 -0
  157. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_optimize.py +0 -0
  158. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_output.py +0 -0
  159. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_parsing_utils.py +0 -0
  160. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_pick.py +0 -0
  161. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_pr.py +0 -0
  162. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_roadmap_baseline_script.py +0 -0
  163. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_sa_plan.py +0 -0
  164. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_sa_unit.py +0 -0
  165. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_schema_catalog.py +0 -0
  166. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_setup.py +0 -0
  167. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_setup_agents.py +0 -0
  168. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_show.py +0 -0
  169. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_snapshot.py +0 -0
  170. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_stash.py +0 -0
  171. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_status.py +0 -0
  172. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_suggest.py +0 -0
  173. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_summarize.py +0 -0
  174. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_sync.py +0 -0
  175. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_sync_changelog_es_script.py +0 -0
  176. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_tag.py +0 -0
  177. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_undo.py +0 -0
  178. {gitwise_cli-0.29.0 → gitwise_cli-0.31.0}/tests/test_worktree.py +0 -0
@@ -3,6 +3,18 @@
3
3
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
4
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## v0.31.0 (2026-06-19)
7
+
8
+ ### Feat
9
+
10
+ - **secret-scan**: commit-time guard and diff --scan-secrets (Sprint 2 D3) (#68)
11
+
12
+ ## v0.30.0 (2026-06-19)
13
+
14
+ ### Feat
15
+
16
+ - **diff**: support refs, path scope, and summary (Sprint 2 PR-A) (#66)
17
+
6
18
  ## v0.29.0 (2026-06-19)
7
19
 
8
20
  ### Feat
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitwise-cli
3
- Version: 0.29.0
3
+ Version: 0.31.0
4
4
  Summary: Python CLI for optimizing git workflows and Claude Code integration
5
5
  Project-URL: Homepage, https://github.com/drzioner/gitwise
6
6
  Project-URL: Repository, https://github.com/drzioner/gitwise
@@ -1,7 +1,10 @@
1
- __version__ = "0.29.0"
1
+ """gitwise -- CLI for optimizing git workflows and coding-agent integration."""
2
+
3
+ __version__ = "0.31.0"
2
4
 
3
5
 
4
6
  def get_version() -> str:
7
+ """Return the installed package version, falling back to the source version."""
5
8
  from importlib.metadata import PackageNotFoundError
6
9
  from importlib.metadata import version as _version
7
10
 
@@ -11,16 +11,19 @@ from .output import print_dim, print_json, set_json_mode, set_json_pretty
11
11
 
12
12
 
13
13
  def _is_log_json_enabled() -> bool:
14
+ """Return True when GITWISE_LOG_JSON is set to a truthy value."""
14
15
  import os
15
16
 
16
17
  return os.environ.get("GITWISE_LOG_JSON", "").lower() in ("1", "true")
17
18
 
18
19
 
19
20
  def _should_show_rich_traceback() -> bool:
21
+ """Return True when rich tracebacks should be used (tty and not in JSON log mode)."""
20
22
  return (not _is_log_json_enabled()) and sys.stderr.isatty()
21
23
 
22
24
 
23
25
  def _install_rich_traceback() -> None:
26
+ """Install the rich traceback handler if conditions allow; no-op otherwise."""
24
27
  if not _should_show_rich_traceback():
25
28
  return
26
29
  try:
@@ -56,6 +59,7 @@ def _ensure_utf8_stdio() -> None:
56
59
 
57
60
 
58
61
  def main() -> int:
62
+ """Parse args, dispatch to the matching subcommand handler, and return an exit code."""
59
63
  import os
60
64
 
61
65
  from ._runtime_config import reset_runtime_config
@@ -7,6 +7,7 @@ from ._cli_parser import build_parser
7
7
 
8
8
 
9
9
  def build_completions_script(*, shell: str, prog: str) -> str:
10
+ """Return a shell completion script for the given shell and program name."""
10
11
  parser = build_parser()
11
12
  parser.prog = prog
12
13
 
@@ -23,10 +24,12 @@ def build_completions_script(*, shell: str, prog: str) -> str:
23
24
 
24
25
 
25
26
  def _fish_escape(text: str) -> str:
27
+ """Escape single quotes for use inside a fish completion string literal."""
26
28
  return text.replace("'", "\\'")
27
29
 
28
30
 
29
31
  def _build_fish_option_line(*, prog: str, condition: str, flag: str, help_text: str) -> str:
32
+ """Return a single fish ``complete`` line for an option, or an empty string for unrecognized flags."""
30
33
  escaped_help = _fish_escape(help_text)
31
34
  escaped_prog = _fish_escape(prog)
32
35
  escaped_condition = _fish_escape(condition)
@@ -44,6 +47,7 @@ def _build_fish_option_line(*, prog: str, condition: str, flag: str, help_text:
44
47
 
45
48
 
46
49
  def _build_fish_completions_script(*, parser: argparse.ArgumentParser, prog: str) -> str:
50
+ """Generate a fish completion script from the full argparse parser tree."""
47
51
  escaped_prog = _fish_escape(prog)
48
52
  lines: list[str] = [f"# fish completion for {escaped_prog}"]
49
53
 
@@ -1,6 +1,7 @@
1
1
  """Command dispatchers: thin wrappers that delegate to subcommand modules."""
2
2
 
3
3
  import argparse
4
+ import sys
4
5
  from collections.abc import Callable
5
6
 
6
7
  from . import __version__
@@ -17,18 +18,21 @@ from .utils.json_envelope import error_envelope
17
18
 
18
19
 
19
20
  def _run_update(args: argparse.Namespace) -> int:
21
+ """Dispatch to ``update`` subcommand."""
20
22
  from .update import run_update
21
23
 
22
24
  return run_update(dry_run=args.dry_run, as_json=args.json)
23
25
 
24
26
 
25
27
  def _run_doctor(args: argparse.Namespace) -> int:
28
+ """Dispatch to ``doctor`` subcommand."""
26
29
  from .doctor import run_doctor
27
30
 
28
31
  return run_doctor(as_json=args.json)
29
32
 
30
33
 
31
34
  def _run_setup_agents(args: argparse.Namespace) -> int:
35
+ """Dispatch to ``setup-agents`` subcommand, handling provider listing."""
32
36
  if getattr(args, "list_providers", False) or getattr(args, "list_adapters", False):
33
37
  from .i18n import t as _t
34
38
  from .setup_agents.providers import list_providers
@@ -68,6 +72,7 @@ def _run_setup_agents(args: argparse.Namespace) -> int:
68
72
 
69
73
 
70
74
  def _run_setup(args: argparse.Namespace) -> int:
75
+ """Dispatch to ``setup`` subcommand."""
71
76
  from .setup import run_setup
72
77
 
73
78
  return run_setup(
@@ -79,24 +84,28 @@ def _run_setup(args: argparse.Namespace) -> int:
79
84
 
80
85
 
81
86
  def _run_audit(args: argparse.Namespace) -> int:
87
+ """Dispatch to ``audit`` subcommand."""
82
88
  from .audit import run_audit
83
89
 
84
90
  return run_audit(quick=args.quick, as_json=args.json)
85
91
 
86
92
 
87
93
  def _run_summarize(args: argparse.Namespace) -> int:
94
+ """Dispatch to ``summarize`` subcommand."""
88
95
  from .summarize import run_summarize
89
96
 
90
97
  return run_summarize(as_json=args.json, diff=args.diff, max_commits=args.max_commits)
91
98
 
92
99
 
93
100
  def _run_snapshot(args: argparse.Namespace) -> int:
101
+ """Dispatch to ``snapshot`` subcommand."""
94
102
  from .snapshot import run_snapshot
95
103
 
96
104
  return run_snapshot(as_json=args.json)
97
105
 
98
106
 
99
107
  def _run_clean(args: argparse.Namespace) -> int:
108
+ """Dispatch to ``clean`` subcommand."""
100
109
  from .clean import run_clean
101
110
 
102
111
  return run_clean(
@@ -109,12 +118,14 @@ def _run_clean(args: argparse.Namespace) -> int:
109
118
 
110
119
 
111
120
  def _run_optimize(args: argparse.Namespace) -> int:
121
+ """Dispatch to ``optimize`` subcommand."""
112
122
  from .optimize import run_optimize
113
123
 
114
124
  return run_optimize(dry_run=args.dry_run, yes=args.yes, as_json=args.json)
115
125
 
116
126
 
117
127
  def _run_worktree(args: argparse.Namespace) -> int:
128
+ """Dispatch to ``worktree`` subcommand."""
118
129
  from .worktree import run_worktree
119
130
 
120
131
  return run_worktree(
@@ -122,19 +133,73 @@ def _run_worktree(args: argparse.Namespace) -> int:
122
133
  )
123
134
 
124
135
 
136
+ def _recover_diff_pathspec_boundary() -> tuple[str | None, list[str] | None]:
137
+ """Recover the explicit ``--`` pathspec boundary argparse loses.
138
+
139
+ With ``refspec nargs="?"`` + ``paths nargs="*"``, a path-only invocation
140
+ like ``gitwise diff -- src/`` mis-assigns ``src/`` to ``refspec``. git
141
+ treats ``--`` as a hard pathspec separator, so honor it here: anything
142
+ positional before ``--`` (excluding flags) feeds ``refspec``; anything
143
+ after ``--`` is literal paths, even values starting with ``-``.
144
+
145
+ Raises ``ValueError`` when more than one positional precedes ``--``, since
146
+ ``gitwise diff`` accepts a single refspec (two-commit diffs use ``a..b``);
147
+ failing fast beats silently dropping the extras.
148
+ """
149
+ try:
150
+ sub_idx = sys.argv.index("diff")
151
+ except ValueError:
152
+ return None, None
153
+ cmd_argv = sys.argv[sub_idx + 1 :]
154
+ if "--" not in cmd_argv:
155
+ return None, None
156
+ sep = cmd_argv.index("--")
157
+ before = [a for a in cmd_argv[:sep] if not a.startswith("-")]
158
+ after = list(cmd_argv[sep + 1 :])
159
+ if len(before) > 1:
160
+ raise ValueError(t("diff_too_many_refs", count=str(len(before))))
161
+ refspec = before[0] if before else None
162
+ paths = after if after else None
163
+ return refspec, paths
164
+
165
+
125
166
  def _run_diff(args: argparse.Namespace) -> int:
167
+ """Dispatch to ``diff`` subcommand."""
126
168
  from .diff import run_diff
169
+ from .output import error as error_out
170
+
171
+ refspec = args.refspec
172
+ paths = args.paths
173
+ # Honor `--` as a pathspec separator (git semantics). argparse's nargs="?"
174
+ # + nargs="*" otherwise mis-assigns path-only invocations to refspec.
175
+ if "--" in sys.argv:
176
+ try:
177
+ recovered_refspec, recovered_paths = _recover_diff_pathspec_boundary()
178
+ except ValueError as exc:
179
+ if args.json:
180
+ print_json(error_envelope(error=str(exc), code="too_many_refs"))
181
+ else:
182
+ error_out(str(exc))
183
+ return 1
184
+ if recovered_refspec is not None or recovered_paths is not None:
185
+ refspec = recovered_refspec
186
+ paths = recovered_paths
127
187
 
128
188
  return run_diff(
189
+ refspec=refspec,
190
+ paths=paths,
129
191
  staged=args.staged,
130
192
  stat=args.stat,
131
193
  name_only=args.name_only,
132
194
  full=args.full,
195
+ summary=args.summary,
196
+ scan_secrets=args.scan_secrets,
133
197
  as_json=args.json,
134
198
  )
135
199
 
136
200
 
137
201
  def _run_log(args: argparse.Namespace) -> int:
202
+ """Dispatch to ``log`` subcommand."""
138
203
  from .log import run_log
139
204
 
140
205
  return run_log(
@@ -151,12 +216,14 @@ def _run_log(args: argparse.Namespace) -> int:
151
216
 
152
217
 
153
218
  def _run_show(args: argparse.Namespace) -> int:
219
+ """Dispatch to ``show`` subcommand."""
154
220
  from .show import run_show
155
221
 
156
222
  return run_show(ref=args.ref, stat=args.stat, as_json=args.json)
157
223
 
158
224
 
159
225
  def _run_commit(args: argparse.Namespace) -> int:
226
+ """Dispatch to ``commit`` subcommand."""
160
227
  from .commit import run_commit
161
228
 
162
229
  return run_commit(
@@ -166,17 +233,20 @@ def _run_commit(args: argparse.Namespace) -> int:
166
233
  breaking=args.breaking,
167
234
  amend=args.amend,
168
235
  dry_run=args.dry_run,
236
+ allow_secret=args.allow_secret,
169
237
  as_json=args.json,
170
238
  )
171
239
 
172
240
 
173
241
  def _run_branches(args: argparse.Namespace) -> int:
242
+ """Dispatch to ``branches`` subcommand."""
174
243
  from .branches import run_branches
175
244
 
176
245
  return run_branches(stale=args.stale, remote=args.remote, sort=args.sort, as_json=args.json)
177
246
 
178
247
 
179
248
  def _run_sync(args: argparse.Namespace) -> int:
249
+ """Dispatch to ``sync`` subcommand."""
180
250
  from .sync import run_sync
181
251
 
182
252
  return run_sync(
@@ -189,12 +259,14 @@ def _run_sync(args: argparse.Namespace) -> int:
189
259
 
190
260
 
191
261
  def _run_pr(args: argparse.Namespace) -> int:
262
+ """Dispatch to ``pr`` subcommand."""
192
263
  from .pr import run_pr
193
264
 
194
265
  return run_pr(action=args.action, selector=args.selector, as_json=args.json)
195
266
 
196
267
 
197
268
  def _run_undo(args: argparse.Namespace) -> int:
269
+ """Dispatch to ``undo`` subcommand."""
198
270
  from .undo import run_undo
199
271
 
200
272
  return run_undo(
@@ -208,18 +280,21 @@ def _run_undo(args: argparse.Namespace) -> int:
208
280
 
209
281
 
210
282
  def _run_context(args: argparse.Namespace) -> int:
283
+ """Dispatch to ``context`` subcommand."""
211
284
  from .context import run_context
212
285
 
213
286
  return run_context(as_json=args.json)
214
287
 
215
288
 
216
289
  def _run_health(args: argparse.Namespace) -> int:
290
+ """Dispatch to ``health`` subcommand."""
217
291
  from .health import run_health
218
292
 
219
293
  return run_health(as_json=args.json)
220
294
 
221
295
 
222
296
  def _run_stash(args: argparse.Namespace) -> int:
297
+ """Dispatch to ``stash`` subcommand."""
223
298
  from .stash import run_stash
224
299
 
225
300
  return run_stash(
@@ -233,6 +308,7 @@ def _run_stash(args: argparse.Namespace) -> int:
233
308
 
234
309
 
235
310
  def _run_tag(args: argparse.Namespace) -> int:
311
+ """Dispatch to ``tag`` subcommand."""
236
312
  from .tag import run_tag
237
313
 
238
314
  return run_tag(
@@ -247,6 +323,7 @@ def _run_tag(args: argparse.Namespace) -> int:
247
323
 
248
324
 
249
325
  def _run_merge(args: argparse.Namespace) -> int:
326
+ """Dispatch to ``merge`` subcommand."""
250
327
  from .merge import run_merge
251
328
 
252
329
  return run_merge(
@@ -262,18 +339,21 @@ def _run_merge(args: argparse.Namespace) -> int:
262
339
 
263
340
 
264
341
  def _run_conflicts(args: argparse.Namespace) -> int:
342
+ """Dispatch to ``conflicts`` subcommand."""
265
343
  from .conflicts import run_conflicts
266
344
 
267
345
  return run_conflicts(ours=args.ours, theirs=args.theirs, as_json=args.json)
268
346
 
269
347
 
270
348
  def _run_suggest(args: argparse.Namespace) -> int:
349
+ """Dispatch to ``suggest`` subcommand."""
271
350
  from .suggest import run_suggest
272
351
 
273
352
  return run_suggest(as_json=args.json)
274
353
 
275
354
 
276
355
  def _run_pick(args: argparse.Namespace) -> int:
356
+ """Dispatch to ``pick`` / ``cherry-pick`` subcommand."""
277
357
  from .pick import run_pick
278
358
 
279
359
  return run_pick(
@@ -287,12 +367,18 @@ def _run_pick(args: argparse.Namespace) -> int:
287
367
 
288
368
 
289
369
  def _run_status(args: argparse.Namespace) -> int:
370
+ """Dispatch to ``status`` subcommand."""
290
371
  from .status import run_status
291
372
 
292
373
  return run_status(as_json=args.json)
293
374
 
294
375
 
295
376
  def _run_completions(args: argparse.Namespace) -> int:
377
+ """Generate and output a shell completions script.
378
+
379
+ Returns 1 on missing dependency (shtab), unsupported shell, or
380
+ runtime error.
381
+ """
296
382
  shell = args.shell
297
383
  prog = args.prog
298
384
  try:
@@ -346,6 +432,7 @@ def _run_completions(args: argparse.Namespace) -> int:
346
432
 
347
433
 
348
434
  def _run_commands(args: argparse.Namespace) -> int:
435
+ """List all registered subcommands with aliases."""
349
436
  parser = build_parser()
350
437
  commands = commands_metadata(parser)
351
438
  payload = {
@@ -374,6 +461,10 @@ def _run_commands(args: argparse.Namespace) -> int:
374
461
 
375
462
 
376
463
  def _run_schema(args: argparse.Namespace) -> int:
464
+ """Print the JSON Schema for a subcommand's CLI input.
465
+
466
+ Returns 1 when the command is unknown or its schema file is missing.
467
+ """
377
468
  from .schema import load_command_input_schema
378
469
 
379
470
  parser = build_parser()
@@ -7,6 +7,7 @@ from . import __version__
7
7
 
8
8
 
9
9
  def _json_safe(value: object) -> object:
10
+ """Recursively convert an argparse value to a JSON-serializable form."""
10
11
  if value is argparse.SUPPRESS:
11
12
  return None
12
13
  if value is None or isinstance(value, str | int | float | bool):
@@ -19,6 +20,7 @@ def _json_safe(value: object) -> object:
19
20
 
20
21
 
21
22
  def _subparsers_action(parser: argparse.ArgumentParser) -> argparse._SubParsersAction | None:
23
+ """Return the first _SubParsersAction from a parser, or None."""
22
24
  for action in parser._actions:
23
25
  if isinstance(action, argparse._SubParsersAction):
24
26
  return action
@@ -26,6 +28,7 @@ def _subparsers_action(parser: argparse.ArgumentParser) -> argparse._SubParsersA
26
28
 
27
29
 
28
30
  def _serialize_actions(parser: argparse.ArgumentParser) -> list[dict[str, object]]:
31
+ """Serialize a parser's non-help, non-subparser actions into JSON-friendly dicts."""
29
32
  items: list[dict[str, object]] = []
30
33
  for action in parser._actions:
31
34
  if action.dest in {"help"}:
@@ -49,6 +52,7 @@ def _serialize_actions(parser: argparse.ArgumentParser) -> list[dict[str, object
49
52
 
50
53
 
51
54
  def extract_command_token(argv: list[str]) -> str | None:
55
+ """Extract the subcommand name from a raw argv, skipping global flags and their values."""
52
56
  skip_next = False
53
57
  for token in argv:
54
58
  if skip_next:
@@ -64,6 +68,7 @@ def extract_command_token(argv: list[str]) -> str | None:
64
68
 
65
69
 
66
70
  def help_payload(parser: argparse.ArgumentParser, command: str | None = None) -> dict[str, object]:
71
+ """Build a structured help payload for the root parser or a specific subcommand."""
67
72
  payload: dict[str, object] = {
68
73
  "v": 2,
69
74
  "ok": True,
@@ -130,6 +135,8 @@ def help_payload(parser: argparse.ArgumentParser, command: str | None = None) ->
130
135
 
131
136
 
132
137
  class CommandMetadata(TypedDict):
138
+ """Minimal metadata for a single subcommand."""
139
+
133
140
  name: str
134
141
  help: str
135
142
  aliases: list[str]
@@ -137,6 +144,7 @@ class CommandMetadata(TypedDict):
137
144
 
138
145
 
139
146
  def canonical_command_name(command_parser: argparse.ArgumentParser) -> str:
147
+ """Return the canonical name (last prog token) of a subcommand parser."""
140
148
  prog = command_parser.prog.strip()
141
149
  if not prog:
142
150
  return ""
@@ -145,6 +153,7 @@ def canonical_command_name(command_parser: argparse.ArgumentParser) -> str:
145
153
 
146
154
 
147
155
  def commands_metadata(parser: argparse.ArgumentParser) -> list[CommandMetadata]:
156
+ """Return metadata for every unique subcommand, deduplicating by parser identity."""
148
157
  sub_action = _subparsers_action(parser)
149
158
  if sub_action is None:
150
159
  return []
@@ -189,6 +198,7 @@ def commands_metadata(parser: argparse.ArgumentParser) -> list[CommandMetadata]:
189
198
 
190
199
 
191
200
  def _json_type_for_action(action: argparse.Action) -> str:
201
+ """Map an argparse action to a JSON Schema type string."""
192
202
  if isinstance(action, argparse._StoreTrueAction | argparse._StoreFalseAction):
193
203
  return "boolean"
194
204
 
@@ -201,6 +211,7 @@ def _json_type_for_action(action: argparse.Action) -> str:
201
211
 
202
212
 
203
213
  def _action_property_schema(action: argparse.Action) -> dict[str, object]:
214
+ """Build a JSON Schema property dict for a single argparse action."""
204
215
  value_schema: dict[str, object] = {"type": _json_type_for_action(action)}
205
216
 
206
217
  if action.choices:
@@ -230,6 +241,7 @@ def _action_property_schema(action: argparse.Action) -> dict[str, object]:
230
241
 
231
242
 
232
243
  def _action_required(action: argparse.Action) -> bool:
244
+ """Determine whether an argparse action produces a required JSON field."""
233
245
  if action.option_strings:
234
246
  return bool(getattr(action, "required", False))
235
247
 
@@ -242,6 +254,7 @@ def _action_required(action: argparse.Action) -> bool:
242
254
 
243
255
 
244
256
  def command_input_schema(command_parser: argparse.ArgumentParser) -> dict[str, object]:
257
+ """Generate a JSON Schema (draft 2020-12) describing the CLI input of a subcommand."""
245
258
  properties: dict[str, object] = {}
246
259
  required: list[str] = []
247
260
 
@@ -269,6 +282,7 @@ def command_input_schema(command_parser: argparse.ArgumentParser) -> dict[str, o
269
282
  def resolve_command_parser(
270
283
  *, parser: argparse.ArgumentParser, name: str
271
284
  ) -> argparse.ArgumentParser | None:
285
+ """Look up the sub-parser registered under *name*, or return None."""
272
286
  sub_action = _subparsers_action(parser)
273
287
  if sub_action is None:
274
288
  return None
@@ -8,10 +8,12 @@ from .i18n import t
8
8
 
9
9
 
10
10
  def _root_help_epilog() -> str:
11
+ """Return the localized environment-variable epilog for root help."""
11
12
  return t("help_root_environment_epilog")
12
13
 
13
14
 
14
15
  def build_parser() -> argparse.ArgumentParser:
16
+ """Build and return the top-level argparse parser with all subcommands registered."""
15
17
  parent = argparse.ArgumentParser(add_help=False)
16
18
  parent.add_argument(
17
19
  "--lang",
@@ -176,7 +178,29 @@ def build_parser() -> argparse.ArgumentParser:
176
178
  diff_group.add_argument(
177
179
  "--full", "--patch", action="store_true", help="show full patch with delta integration"
178
180
  )
181
+ diff_group.add_argument(
182
+ "--summary",
183
+ action="store_true",
184
+ help="compact summary: additions/deletions per file, no patch",
185
+ )
179
186
  p.add_argument("--stat", action="store_true", help="show diffstat (default behavior)")
187
+ p.add_argument(
188
+ "--scan-secrets",
189
+ action="store_true",
190
+ help="scan the diff for leaked credentials (advisory, opt-in)",
191
+ )
192
+ p.add_argument(
193
+ "refspec",
194
+ nargs="?",
195
+ default=None,
196
+ help="commit, branch, or range to diff (e.g. HEAD~3, main..feat, main...feat)",
197
+ )
198
+ p.add_argument(
199
+ "paths",
200
+ nargs="*",
201
+ metavar="PATH",
202
+ help="limit to paths (use -- to separate from refspec)",
203
+ )
180
204
 
181
205
  p = sub.add_parser("log", help="pretty git log with filters", parents=[parent])
182
206
  p.add_argument("--oneline", action="store_true", help="one line per commit")
@@ -199,6 +223,11 @@ def build_parser() -> argparse.ArgumentParser:
199
223
  p.add_argument("--breaking", action="store_true", help="breaking change (!)")
200
224
  p.add_argument("--amend", action="store_true", help="amend last commit")
201
225
  p.add_argument("--dry-run", action="store_true", help="show without committing")
226
+ p.add_argument(
227
+ "--allow-secret",
228
+ action="store_true",
229
+ help="proceed past a high-severity secret finding (with confirmation)",
230
+ )
202
231
 
203
232
  p = sub.add_parser("branches", help="branch intelligence dashboard", parents=[parent])
204
233
  p.add_argument("--stale", action="store_true", help="show stale [gone] branches only")
@@ -194,6 +194,11 @@ def _run_setup_local(
194
194
  providers: list[str] | None = None,
195
195
  adapters_legacy_used: bool = False,
196
196
  ) -> int:
197
+ """Install per-repo setup-agents artifacts (CLAUDE.md, settings, skills).
198
+
199
+ Returns 0 on success, 1 on plan/execution errors, 2 when ``--strict``
200
+ mode encounters warnings.
201
+ """
197
202
  cwd = target or Path.cwd()
198
203
 
199
204
  if not is_repo(cwd):
@@ -447,10 +447,54 @@
447
447
  "es": "Diagnóstico{suffix} — {count} observación(es):",
448
448
  "en": "Diagnostic{suffix} — {count} finding(s):"
449
449
  },
450
+ "diff_binary_lfs_hint": {
451
+ "es": "archivo binario {path} ({mib} MiB) -- considera Git LFS",
452
+ "en": "binary file {path} ({mib} MiB) -- consider Git LFS"
453
+ },
450
454
  "diff_prefix": {
451
455
  "es": "diff: {stat}",
452
456
  "en": "diff: {stat}"
453
457
  },
458
+ "diff_summary_header": {
459
+ "es": "Resumen -- {count} archivo(s):",
460
+ "en": "Summary -- {count} file(s):"
461
+ },
462
+ "diff_too_many_refs": {
463
+ "es": "demasiados argumentos posicionales antes de '--' ({count}); gitwise diff acepta un unico refspec (usa a..b para un rango)",
464
+ "en": "too many positional arguments before '--' ({count}); gitwise diff accepts a single refspec (use a..b for a range)"
465
+ },
466
+ "secret_scan_clean": {
467
+ "es": "no se detectaron secretos en el diff",
468
+ "en": "no secrets detected in the diff"
469
+ },
470
+ "secret_scan_found": {
471
+ "es": "{rule} en {path}:{line} ({preview})",
472
+ "en": "{rule} at {path}:{line} ({preview})"
473
+ },
474
+ "secret_scan_found_header": {
475
+ "es": "Secretos detectados -- {count} hallazgo(s):",
476
+ "en": "Secrets detected -- {count} finding(s):"
477
+ },
478
+ "secret_scan_blocked_high": {
479
+ "es": "commit bloqueado: {count} secreto(s) de alta severidad en el área de staging",
480
+ "en": "commit blocked: {count} high-severity secret(s) in the staging area"
481
+ },
482
+ "secret_scan_blocked_hint": {
483
+ "es": "revoque o rote el secreto, remuévalo del stage, o use --allow-secret (con confirmación) para commitear de todos modos",
484
+ "en": "revoke/rotate the secret, unstage it, or use --allow-secret (with confirmation) to commit anyway"
485
+ },
486
+ "secret_scan_allow_confirm": {
487
+ "es": "Se detectaron {count} secreto(s) de alta severidad. ¿Commitear de todos modos?",
488
+ "en": "{count} high-severity secret(s) detected. Commit anyway?"
489
+ },
490
+ "secret_scan_medium_warning": {
491
+ "es": "advertencia: {rule} en {path}:{line} ({preview})",
492
+ "en": "warning: {rule} at {path}:{line} ({preview})"
493
+ },
494
+ "secret_scan_unavailable": {
495
+ "es": "no se pudo escanear el área de staging (fail-closed): {error}",
496
+ "en": "could not scan the staging area (fail-closed): {error}"
497
+ },
454
498
  "directory_exists": {
455
499
  "es": "el directorio ya existe: {path}",
456
500
  "en": "directory already exists: {path}"