mycli 2.11.0__tar.gz → 2.12.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 (189) hide show
  1. {mycli-2.11.0/mycli.egg-info → mycli-2.12.0}/PKG-INFO +5 -5
  2. {mycli-2.11.0 → mycli-2.12.0}/README.md +3 -3
  3. {mycli-2.11.0 → mycli-2.12.0}/changelog.md +31 -0
  4. {mycli-2.11.0 → mycli-2.12.0}/mycli/TIPS +2 -0
  5. {mycli-2.11.0 → mycli-2.12.0}/mycli/client.py +9 -2
  6. {mycli-2.11.0 → mycli-2.12.0}/mycli/client_commands.py +151 -14
  7. {mycli-2.11.0 → mycli-2.12.0}/mycli/client_query.py +1 -0
  8. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/repl.py +3 -1
  9. {mycli-2.11.0 → mycli-2.12.0}/mycli/myclirc +6 -0
  10. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/completion_engine.py +15 -0
  11. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/__init__.py +2 -0
  12. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/dsn_aliases.py +42 -3
  13. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/main.py +6 -1
  14. {mycli-2.11.0 → mycli-2.12.0}/mycli/sqlcompleter.py +15 -2
  15. {mycli-2.11.0 → mycli-2.12.0/mycli.egg-info}/PKG-INFO +5 -5
  16. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/requires.txt +1 -1
  17. mycli-2.12.0/mycli.egg-info/scm_version.json +8 -0
  18. {mycli-2.11.0 → mycli-2.12.0}/pyproject.toml +3 -1
  19. {mycli-2.11.0 → mycli-2.12.0}/test/features/basic_commands.feature +1 -1
  20. {mycli-2.11.0 → mycli-2.12.0}/test/features/connection.feature +6 -4
  21. mycli-2.12.0/test/features/fixture_data/help_commands.txt +41 -0
  22. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/basic_commands.py +23 -1
  23. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/connection.py +9 -7
  24. {mycli-2.11.0 → mycli-2.12.0}/test/myclirc +6 -0
  25. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_client.py +58 -1
  26. mycli-2.12.0/test/pytests/test_client_commands.py +754 -0
  27. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_client_query.py +4 -0
  28. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_completion_engine.py +15 -0
  29. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_dsn_aliases.py +144 -0
  30. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_main.py +28 -0
  31. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_main_modes_repl.py +25 -0
  32. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_main.py +6 -0
  33. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_sqlcompleter.py +25 -0
  34. mycli-2.11.0/mycli.egg-info/scm_version.json +0 -8
  35. mycli-2.11.0/test/features/fixture_data/help_commands.txt +0 -40
  36. mycli-2.11.0/test/pytests/test_client_commands.py +0 -340
  37. {mycli-2.11.0 → mycli-2.12.0}/.git-blame-ignore-revs +0 -0
  38. {mycli-2.11.0 → mycli-2.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  39. {mycli-2.11.0 → mycli-2.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  40. {mycli-2.11.0 → mycli-2.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  41. {mycli-2.11.0 → mycli-2.12.0}/.github/dependabot.yml +0 -0
  42. {mycli-2.11.0 → mycli-2.12.0}/.github/workflows/ci.yml +0 -0
  43. {mycli-2.11.0 → mycli-2.12.0}/.github/workflows/lint.yml +0 -0
  44. {mycli-2.11.0 → mycli-2.12.0}/.github/workflows/publish.yml +0 -0
  45. {mycli-2.11.0 → mycli-2.12.0}/.github/workflows/typecheck.yml +0 -0
  46. {mycli-2.11.0 → mycli-2.12.0}/.gitignore +0 -0
  47. {mycli-2.11.0 → mycli-2.12.0}/AGENTS.md +0 -0
  48. {mycli-2.11.0 → mycli-2.12.0}/AUTHORS.rst +0 -0
  49. {mycli-2.11.0 → mycli-2.12.0}/CONTRIBUTING.md +0 -0
  50. {mycli-2.11.0 → mycli-2.12.0}/LICENSE.txt +0 -0
  51. {mycli-2.11.0 → mycli-2.12.0}/MANIFEST.in +0 -0
  52. {mycli-2.11.0 → mycli-2.12.0}/SPONSORS.rst +0 -0
  53. {mycli-2.11.0 → mycli-2.12.0}/doc/key_bindings.rst +0 -0
  54. {mycli-2.11.0 → mycli-2.12.0}/doc/llm.md +0 -0
  55. {mycli-2.11.0 → mycli-2.12.0}/doc/screenshots/main.gif +0 -0
  56. {mycli-2.11.0 → mycli-2.12.0}/doc/screenshots/tables.png +0 -0
  57. {mycli-2.11.0 → mycli-2.12.0}/doc/screenshots/total_histogram.png +0 -0
  58. {mycli-2.11.0 → mycli-2.12.0}/doc/transforms.md +0 -0
  59. {mycli-2.11.0 → mycli-2.12.0}/mycli/AUTHORS +0 -0
  60. {mycli-2.11.0 → mycli-2.12.0}/mycli/SPONSORS +0 -0
  61. {mycli-2.11.0 → mycli-2.12.0}/mycli/__init__.py +0 -0
  62. {mycli-2.11.0 → mycli-2.12.0}/mycli/app_state.py +0 -0
  63. {mycli-2.11.0 → mycli-2.12.0}/mycli/cli_runner.py +0 -0
  64. {mycli-2.11.0 → mycli-2.12.0}/mycli/clibuffer.py +0 -0
  65. {mycli-2.11.0 → mycli-2.12.0}/mycli/client_connection.py +0 -0
  66. {mycli-2.11.0 → mycli-2.12.0}/mycli/clistyle.py +0 -0
  67. {mycli-2.11.0 → mycli-2.12.0}/mycli/clitoolbar.py +0 -0
  68. {mycli-2.11.0 → mycli-2.12.0}/mycli/compat.py +0 -0
  69. {mycli-2.11.0 → mycli-2.12.0}/mycli/completion_refresher.py +0 -0
  70. {mycli-2.11.0 → mycli-2.12.0}/mycli/config.py +0 -0
  71. {mycli-2.11.0 → mycli-2.12.0}/mycli/constants.py +0 -0
  72. {mycli-2.11.0 → mycli-2.12.0}/mycli/key_bindings.py +0 -0
  73. {mycli-2.11.0 → mycli-2.12.0}/mycli/lexer.py +0 -0
  74. {mycli-2.11.0 → mycli-2.12.0}/mycli/main.py +0 -0
  75. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/batch.py +0 -0
  76. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/checkup.py +0 -0
  77. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/completions.py +0 -0
  78. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/execute.py +0 -0
  79. {mycli-2.11.0 → mycli-2.12.0}/mycli/main_modes/list_dsn.py +0 -0
  80. {mycli-2.11.0 → mycli-2.12.0}/mycli/output.py +0 -0
  81. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/__init__.py +0 -0
  82. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/batch_utils.py +0 -0
  83. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/cli_utils.py +0 -0
  84. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/filepaths.py +0 -0
  85. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/hybrid_redirection.py +0 -0
  86. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/interactive_utils.py +0 -0
  87. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/key_binding_utils.py +0 -0
  88. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/polars_transform.py +0 -0
  89. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  90. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  91. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/ptoolkit/history.py +0 -0
  92. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/ptoolkit/utils.py +0 -0
  93. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/dbcommands.py +0 -0
  94. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/delimitercommand.py +0 -0
  95. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/favoritequeries.py +0 -0
  96. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/iocommands.py +0 -0
  97. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/llm.py +0 -0
  98. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/special/utils.py +0 -0
  99. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/sql_utils.py +0 -0
  100. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/sqlresult.py +0 -0
  101. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/string_utils.py +0 -0
  102. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/tabular_output/__init__.py +0 -0
  103. {mycli-2.11.0 → mycli-2.12.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  104. {mycli-2.11.0 → mycli-2.12.0}/mycli/password_sources.py +0 -0
  105. {mycli-2.11.0 → mycli-2.12.0}/mycli/resources/completions/bash/mycli +0 -0
  106. {mycli-2.11.0 → mycli-2.12.0}/mycli/resources/completions/fish/mycli.fish +0 -0
  107. {mycli-2.11.0 → mycli-2.12.0}/mycli/resources/completions/zsh/_mycli +0 -0
  108. {mycli-2.11.0 → mycli-2.12.0}/mycli/schema_prefetcher.py +0 -0
  109. {mycli-2.11.0 → mycli-2.12.0}/mycli/sqlexecute.py +0 -0
  110. {mycli-2.11.0 → mycli-2.12.0}/mycli/ssh_tunnel.py +0 -0
  111. {mycli-2.11.0 → mycli-2.12.0}/mycli/types.py +0 -0
  112. {mycli-2.11.0 → mycli-2.12.0}/mycli/vault.py +0 -0
  113. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/SOURCES.txt +0 -0
  114. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/dependency_links.txt +0 -0
  115. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/entry_points.txt +0 -0
  116. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/scm_file_list.json +0 -0
  117. {mycli-2.11.0 → mycli-2.12.0}/mycli.egg-info/top_level.txt +0 -0
  118. {mycli-2.11.0 → mycli-2.12.0}/setup.cfg +0 -0
  119. {mycli-2.11.0 → mycli-2.12.0}/test/__init__.py +0 -0
  120. {mycli-2.11.0 → mycli-2.12.0}/test/features/__init__.py +0 -0
  121. {mycli-2.11.0 → mycli-2.12.0}/test/features/auto_vertical.feature +0 -0
  122. {mycli-2.11.0 → mycli-2.12.0}/test/features/crud_database.feature +0 -0
  123. {mycli-2.11.0 → mycli-2.12.0}/test/features/crud_table.feature +0 -0
  124. {mycli-2.11.0 → mycli-2.12.0}/test/features/db_utils.py +0 -0
  125. {mycli-2.11.0 → mycli-2.12.0}/test/features/environment.py +0 -0
  126. {mycli-2.11.0 → mycli-2.12.0}/test/features/fixture_utils.py +0 -0
  127. {mycli-2.11.0 → mycli-2.12.0}/test/features/iocommands.feature +0 -0
  128. {mycli-2.11.0 → mycli-2.12.0}/test/features/named_queries.feature +0 -0
  129. {mycli-2.11.0 → mycli-2.12.0}/test/features/specials.feature +0 -0
  130. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/__init__.py +0 -0
  131. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/auto_vertical.py +0 -0
  132. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/crud_database.py +0 -0
  133. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/crud_table.py +0 -0
  134. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/iocommands.py +0 -0
  135. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/named_queries.py +0 -0
  136. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/specials.py +0 -0
  137. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/utils.py +0 -0
  138. {mycli-2.11.0 → mycli-2.12.0}/test/features/steps/wrappers.py +0 -0
  139. {mycli-2.11.0 → mycli-2.12.0}/test/features/wrappager.py +0 -0
  140. {mycli-2.11.0 → mycli-2.12.0}/test/mylogin.cnf +0 -0
  141. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/conftest.py +0 -0
  142. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_app_state.py +0 -0
  143. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_bash_completion.py +0 -0
  144. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_batch_utils.py +0 -0
  145. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_checkup.py +0 -0
  146. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_cli_runner.py +0 -0
  147. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_cli_utils.py +0 -0
  148. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_clibuffer.py +0 -0
  149. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_client_connection.py +0 -0
  150. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_clistyle.py +0 -0
  151. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_clitoolbar.py +0 -0
  152. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_completer_use_switch.py +0 -0
  153. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_completion_refresher.py +0 -0
  154. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_config.py +0 -0
  155. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_delimitercommand.py +0 -0
  156. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_favoritequeries.py +0 -0
  157. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_filepaths.py +0 -0
  158. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_fish_completion.py +0 -0
  159. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_hybrid_redirection.py +0 -0
  160. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_interactive_utils.py +0 -0
  161. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_key_binding_utils.py +0 -0
  162. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_key_bindings.py +0 -0
  163. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_lexer.py +0 -0
  164. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_main_modes_batch.py +0 -0
  165. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_main_modes_execute.py +0 -0
  166. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  167. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_naive_completion.py +0 -0
  168. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_output.py +0 -0
  169. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_password_sources.py +0 -0
  170. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_polars_transform.py +0 -0
  171. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  172. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_ptoolkit_history.py +0 -0
  173. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  174. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_schema_prefetcher.py +0 -0
  175. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  176. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_dbcommands.py +0 -0
  177. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_init.py +0 -0
  178. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_iocommands.py +0 -0
  179. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_llm.py +0 -0
  180. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_special_utils.py +0 -0
  181. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_sql_utils.py +0 -0
  182. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_sqlexecute.py +0 -0
  183. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_sqlresult.py +0 -0
  184. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_ssh_tunnel.py +0 -0
  185. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_string_utils.py +0 -0
  186. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_tabular_output.py +0 -0
  187. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_vault.py +0 -0
  188. {mycli-2.11.0 → mycli-2.12.0}/test/pytests/test_zsh_completion.py +0 -0
  189. {mycli-2.11.0 → mycli-2.12.0}/test/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.11.0
3
+ Version: 2.12.0
4
4
  Summary: CLI for MySQL Database. With auto-completion and syntax highlighting.
5
5
  Author: Mycli Core Team
6
6
  License-Expression: BSD-3-Clause
@@ -28,7 +28,7 @@ Requires-Python: >=3.10
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE.txt
30
30
  License-File: AUTHORS.rst
31
- Requires-Dist: click~=8.4.2
31
+ Requires-Dist: click~=8.3.3
32
32
  Requires-Dist: clickdc~=0.1.1
33
33
  Requires-Dist: cryptography~=50.0.0
34
34
  Requires-Dist: Pygments~=2.20.0
@@ -147,14 +147,14 @@ Features
147
147
  * Pretty print tabular data (with colors!).
148
148
  * Support for SSL connections
149
149
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
150
- * [Polars](https://pola.rs) dataframe [transforms and plots](doc/transforms.md) with `.|`, and Parquet saves with `.>`.
151
- * Support for querying LLMs with context derived from your schema using `/llm`.
150
+ * [Polars](https://pola.rs) dataframe [transforms and plots](https://github.com/dbcli/mycli/blob/main/doc/transforms.md) with `.|`, and Parquet saves with `.>`.
151
+ * Support for [querying LLMs](https://www.mycli.net/llm) with context derived from your schema using `/llm`.
152
152
  * Support for storing passwords in the system keyring.
153
153
 
154
154
  Mycli creates a config file `~/.myclirc` on the first run; you can use the
155
155
  options in that file to configure the above features, and more.
156
156
 
157
- Some features are only exposed as [key bindings](doc/key_bindings.rst).
157
+ Some features are only exposed as [key bindings](https://github.com/dbcli/mycli/blob/main/doc/key_bindings.rst).
158
158
 
159
159
 
160
160
  Implementation
@@ -75,14 +75,14 @@ Features
75
75
  * Pretty print tabular data (with colors!).
76
76
  * Support for SSL connections
77
77
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
78
- * [Polars](https://pola.rs) dataframe [transforms and plots](doc/transforms.md) with `.|`, and Parquet saves with `.>`.
79
- * Support for querying LLMs with context derived from your schema using `/llm`.
78
+ * [Polars](https://pola.rs) dataframe [transforms and plots](https://github.com/dbcli/mycli/blob/main/doc/transforms.md) with `.|`, and Parquet saves with `.>`.
79
+ * Support for [querying LLMs](https://www.mycli.net/llm) with context derived from your schema using `/llm`.
80
80
  * Support for storing passwords in the system keyring.
81
81
 
82
82
  Mycli creates a config file `~/.myclirc` on the first run; you can use the
83
83
  options in that file to configure the above features, and more.
84
84
 
85
- Some features are only exposed as [key bindings](doc/key_bindings.rst).
85
+ Some features are only exposed as [key bindings](https://github.com/dbcli/mycli/blob/main/doc/key_bindings.rst).
86
86
 
87
87
 
88
88
  Implementation
@@ -1,3 +1,34 @@
1
+ 2.12.0 (2026/08/08)
2
+ ==============
3
+
4
+ Breaking Changes
5
+ ---------
6
+ * `/source` scripts can no longer contain special /commands, but only SQL.
7
+
8
+
9
+ Features
10
+ ---------
11
+ * Allow reading DSN aliases from a shared configuration file.
12
+ * Add a `/config` command to inspect configuration values from the REPL.
13
+
14
+
15
+ Bugfixes
16
+ ---------
17
+ * Downgrade `click` to v8.3.3, since v8.4.2 broke the pager on Windows.
18
+ * Don't read `/source` targets entirely into memory.
19
+ * Continue with a `/source` script after declining a destructive query.
20
+
21
+
22
+ Documentation
23
+ ---------
24
+ * Make all links in `README.md` absolute, so that they don't break on PyPi.
25
+
26
+
27
+ Internal
28
+ ---------
29
+ * Tiny change to quoting in `client_commands.py`.
30
+
31
+
1
32
  2.11.0 (2026/08/07)
2
33
  ==============
3
34
 
@@ -114,6 +114,8 @@ use /bug to file a bug on GitHub!
114
114
 
115
115
  use /dsn to manage saved DSNs!
116
116
 
117
+ use /config to inspect persistent configuration from the REPL!
118
+
117
119
  ###
118
120
  ### environment variables
119
121
  ###
@@ -20,7 +20,7 @@ from mycli.app_state import (
20
20
  normalize_image_protocol,
21
21
  normalize_ssl_mode,
22
22
  )
23
- from mycli.client_commands import ClientCommandsMixin
23
+ from mycli.client_commands import ClientCommandsMixin, get_config_property_names
24
24
  from mycli.client_connection import ClientConnectionMixin
25
25
  from mycli.client_query import ClientQueryMixin
26
26
  from mycli.clistyle import style_factory_helpers, style_factory_ptoolkit
@@ -93,6 +93,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
93
93
  myclirc = self.xdg_config_file
94
94
  else:
95
95
  myclirc = '~/.myclirc'
96
+ self.myclirc_path = os.path.abspath(os.path.expanduser(myclirc))
96
97
 
97
98
  # Load config.
98
99
  config_files: list[str | IO[str]] = self.system_config_files + [myclirc]
@@ -120,7 +121,12 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
120
121
  myclirc,
121
122
  c['main'].get('shared_favorites_file'),
122
123
  )
123
- DsnAliases.instance = DsnAliases.from_config(self.config, self, config_file=myclirc)
124
+ DsnAliases.instance = DsnAliases.from_config(
125
+ self.config,
126
+ self,
127
+ config_file=myclirc,
128
+ shared_dsns_file=c['main'].get('shared_dsns_file'),
129
+ )
124
130
 
125
131
  self.dsn_alias: str | None = None
126
132
  self.main_formatter = TabularOutputFormatter(format_name=c["main"]["table_format"])
@@ -200,6 +206,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
200
206
  supported_formats=self.main_formatter.supported_formats,
201
207
  keyword_casing=keyword_casing,
202
208
  indexed_column_suffix=indexed_column_suffix,
209
+ config_property_names=get_config_property_names(self.config),
203
210
  )
204
211
  self._completer_lock = threading.Lock()
205
212
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Generator, Iterable
3
+ from collections.abc import Generator, Mapping
4
4
  import logging
5
5
  import os
6
6
  import re
@@ -8,14 +8,82 @@ from typing import TYPE_CHECKING, Any, cast
8
8
 
9
9
  import click
10
10
 
11
+ from mycli.config import write_default_config
11
12
  from mycli.main_modes.repl import set_all_external_titles
12
13
  from mycli.packages import special
14
+ from mycli.packages.batch_utils import statements_from_filehandle
13
15
  from mycli.packages.filepaths import dir_path_exists
14
16
  from mycli.packages.interactive_utils import confirm_destructive_query
15
17
  from mycli.packages.special.main import ArgType, SpecialCommandAlias
16
18
  from mycli.packages.sqlresult import SQLResult
17
19
  from mycli.sqlexecute import SQLExecute
18
20
 
21
+ CONFIG_COMMAND_USAGE = '''Syntax:
22
+ /config get <key>
23
+ /config search <regex>
24
+ /config edit
25
+ Examples:
26
+ /config get main.show_warnings
27
+ /config search warning
28
+ /config edit'''
29
+ MISSING_CONFIG_VALUE = object()
30
+ DSN_CONFIG_VALUE = object()
31
+ FAVORITES_CONFIG_VALUE = object()
32
+ HIDDEN_CONFIG_SECTIONS = frozenset({'alias_dsn', 'favorite_queries'})
33
+
34
+
35
+ def _render_config_value(value: Any) -> str:
36
+ if isinstance(value, list):
37
+ return ', '.join(str(item) for item in value)
38
+ return str(value)
39
+
40
+
41
+ def _iter_config_values(
42
+ config: Mapping[str, Any],
43
+ prefix: str = '',
44
+ ) -> Generator[tuple[str, str], None, None]:
45
+ """Yield dotted paths and rendered values for searchable settings."""
46
+ for key, value in config.items():
47
+ if not prefix and key in HIDDEN_CONFIG_SECTIONS:
48
+ continue
49
+
50
+ path = f'{prefix}.{key}' if prefix else key
51
+ if isinstance(value, Mapping):
52
+ yield from _iter_config_values(value, path)
53
+ else:
54
+ yield path, _render_config_value(value)
55
+
56
+
57
+ def get_config_property_names(config: Mapping[str, Any]) -> list[str]:
58
+ """Return sorted leaf paths available to the config command."""
59
+ return sorted(path for path, _value in _iter_config_values(config))
60
+
61
+
62
+ def _resolve_config_path(config: Mapping[str, Any], path: str) -> Any:
63
+ """Resolve a dotted path while allowing dots within mapping keys."""
64
+ parts = path.split('.')
65
+ value: Any = config
66
+ top_level = True
67
+ while parts:
68
+ if not isinstance(value, Mapping):
69
+ return MISSING_CONFIG_VALUE
70
+
71
+ for part_count in range(len(parts), 0, -1):
72
+ key = '.'.join(parts[:part_count])
73
+ if key in value:
74
+ if top_level and key == 'alias_dsn':
75
+ return DSN_CONFIG_VALUE
76
+ if top_level and key == 'favorite_queries':
77
+ return FAVORITES_CONFIG_VALUE
78
+ value = value[key]
79
+ parts = parts[part_count:]
80
+ top_level = False
81
+ break
82
+ else:
83
+ return MISSING_CONFIG_VALUE
84
+
85
+ return value
86
+
19
87
 
20
88
  class ClientCommandsMixin:
21
89
  if TYPE_CHECKING:
@@ -25,6 +93,7 @@ class ClientCommandsMixin:
25
93
  destructive_warning: bool
26
94
  destructive_keywords: Any
27
95
  config: Any
96
+ myclirc_path: str
28
97
  prompt_format: str
29
98
 
30
99
  def refresh_completions(self, reset: bool = False) -> list[SQLResult]: ...
@@ -86,6 +155,12 @@ class ClientCommandsMixin:
86
155
  case_sensitive=True,
87
156
  aliases=[SpecialCommandAlias("\\R", case_sensitive=True)],
88
157
  )
158
+ special.register_special_command(
159
+ self.config_command,
160
+ r'\config',
161
+ '/config <help|get|search|edit> [key]',
162
+ 'Inspect settings from config files.',
163
+ )
89
164
 
90
165
  def manual_reconnect(self, arg: str = "", **_) -> Generator[SQLResult, None, None]:
91
166
  """
@@ -119,6 +194,54 @@ class ClientCommandsMixin:
119
194
  msg += f"\n\t{table_type}"
120
195
  yield SQLResult(status=msg)
121
196
 
197
+ def config_command(self, arg: str, **_) -> list[SQLResult]:
198
+ args = arg.strip().split(maxsplit=1)
199
+ if not args or args[0].lower() == 'help':
200
+ return [SQLResult(preamble=CONFIG_COMMAND_USAGE)]
201
+
202
+ subcommand = args[0].lower()
203
+ if subcommand == 'edit' and len(args) == 1:
204
+ return self.edit_config_file()
205
+
206
+ if subcommand == 'get' and len(args) == 2 and len(args[1].split()) == 1:
207
+ path = args[1]
208
+ value = _resolve_config_path(self.config, path)
209
+ if value is MISSING_CONFIG_VALUE:
210
+ return [SQLResult(status=f'Config key not found: {path}.')]
211
+ if value is DSN_CONFIG_VALUE:
212
+ return [SQLResult(status='See "/dsn list" for DSNs.')]
213
+ if value is FAVORITES_CONFIG_VALUE:
214
+ return [SQLResult(status='See "/f" for favorite queries.')]
215
+ if isinstance(value, Mapping):
216
+ return [SQLResult(status=f'Config path is not a value: {path}.')]
217
+ return [SQLResult(header=['Key', 'Value'], rows=[(path, _render_config_value(value))])]
218
+
219
+ if subcommand == 'search' and len(args) == 2:
220
+ pattern_text = args[1]
221
+ try:
222
+ pattern = re.compile(pattern_text, re.IGNORECASE)
223
+ except re.error as error:
224
+ return [SQLResult(status=f'Invalid regular expression: {error}.')]
225
+
226
+ rows = sorted(
227
+ (path, value) for path, value in _iter_config_values(self.config) if pattern.search(path) or pattern.search(value)
228
+ )
229
+ if not rows:
230
+ return [SQLResult(status=f'No configuration values match "{pattern_text}".')]
231
+ return [SQLResult(header=['Key', 'Value'], rows=rows)]
232
+
233
+ return [SQLResult(preamble=CONFIG_COMMAND_USAGE)]
234
+
235
+ def edit_config_file(self) -> list[SQLResult]:
236
+ try:
237
+ write_default_config(self.myclirc_path, overwrite=False)
238
+ click.edit(filename=self.myclirc_path)
239
+ except KeyboardInterrupt:
240
+ return [SQLResult(status='Config edit cancelled.')]
241
+ except (click.ClickException, OSError) as error:
242
+ return [SQLResult(status=f'Unable to edit config file "{self.myclirc_path}": {error}')]
243
+ return [SQLResult(status=f'Config file edited: {self.myclirc_path}. Restart mycli to apply changes.')]
244
+
122
245
  def change_db(self, arg: str, **_) -> Generator[SQLResult, None, None]:
123
246
  if arg.startswith("`") and arg.endswith("`"):
124
247
  arg = re.sub(r"^`(.*)`$", r"\1", arg)
@@ -142,22 +265,36 @@ class ClientCommandsMixin:
142
265
 
143
266
  yield SQLResult(status=msg)
144
267
 
145
- def execute_from_file(self, arg: str, **_) -> Iterable[SQLResult]:
268
+ def execute_from_file(self, arg: str, **_) -> Generator[SQLResult, None, None]:
146
269
  if not arg:
147
- message = "Missing required argument: filename."
148
- return [SQLResult(status=message)]
149
- try:
150
- with open(os.path.expanduser(arg)) as f:
151
- query = f.read()
152
- except IOError as e:
153
- return [SQLResult(status=str(e))]
270
+ yield SQLResult(status="Missing required argument: filename.")
271
+ return
154
272
 
155
- if self.destructive_warning and confirm_destructive_query(self.destructive_keywords, query) is False:
156
- message = "Wise choice. Command execution stopped."
157
- return [SQLResult(status=message)]
273
+ try:
274
+ file_h = open(os.path.expanduser(arg))
275
+ except OSError as error:
276
+ yield SQLResult(status=str(error))
277
+ return
158
278
 
159
279
  assert isinstance(self.sqlexecute, SQLExecute)
160
- return self.sqlexecute.run(query)
280
+ with file_h:
281
+ statements = statements_from_filehandle(file_h)
282
+ while True:
283
+ try:
284
+ query, _counter = next(statements)
285
+ except StopIteration:
286
+ return
287
+ except (OSError, ValueError) as error:
288
+ yield SQLResult(status=str(error))
289
+ return
290
+
291
+ if special.is_special_command(query.rstrip(';')):
292
+ yield SQLResult(status='Special commands are not supported in source files.')
293
+ return
294
+
295
+ if self.destructive_warning and confirm_destructive_query(self.destructive_keywords, query) is False:
296
+ continue
297
+ yield from self.sqlexecute.run(query)
161
298
 
162
299
  def change_prompt_format(self, arg: str, **_) -> list[SQLResult]:
163
300
  """
@@ -166,7 +303,7 @@ class ClientCommandsMixin:
166
303
  if not arg:
167
304
  return [SQLResult(status=f'Prompt format: "{self.prompt_format}"')]
168
305
 
169
- if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] in {'\'', '"'}:
306
+ if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] in {"'", '"'}:
170
307
  arg = arg[1:-1]
171
308
 
172
309
  self.prompt_format = arg
@@ -53,6 +53,7 @@ class ClientQueryMixin:
53
53
  "supported_formats": self.main_formatter.supported_formats,
54
54
  "keyword_casing": self.completer.keyword_casing,
55
55
  "indexed_column_suffix": self.completer.indexed_column_suffix,
56
+ "config_property_names": self.completer.config_property_names,
56
57
  },
57
58
  )
58
59
 
@@ -119,6 +119,7 @@ def complete_while_typing_filter() -> bool:
119
119
  text = app.current_buffer.text.lstrip()
120
120
  if text.startswith('/') and not text.startswith('/*') and ' ' not in text:
121
121
  return True
122
+ completing_config_property = bool(re.match(r'^(?:/|\\)config\s+get\s+', text, re.IGNORECASE))
122
123
  text_len = len(text)
123
124
  if text_len < MIN_COMPLETION_TRIGGER:
124
125
  return False
@@ -137,7 +138,8 @@ def complete_while_typing_filter() -> bool:
137
138
  # limit. We would have to parse the statement, or at least go back more
138
139
  # characters, costing performance. This still works within a backtick! So
139
140
  # long as there are three trailing non-punctuation characters.
140
- return not bool(re.search(r'[\s!-/:-@\[-^\{-~]', last_word))
141
+ punctuation_check = last_word.replace('.', '') if completing_config_property else last_word
142
+ return not bool(re.search(r'[\s!-/:-@\[-^\{-~]', punctuation_check))
141
143
 
142
144
 
143
145
  def _create_history(mycli: 'MyCli') -> FileHistoryWithTimestamp | None:
@@ -77,6 +77,12 @@ show_favorite_query = True
77
77
  # Example: /usr/local/etc/mycli/shared-favorites.ini
78
78
  shared_favorites_file =
79
79
 
80
+ # Load additional DSN aliases from the [alias_dsn] section of this file. DSN
81
+ # aliases in the user's configuration file take precedence over the shared
82
+ # file. The path must be absolute after expanding ~.
83
+ # Example: /usr/local/etc/mycli/shared-dsns.ini
84
+ shared_dsns_file =
85
+
80
86
  # Beep after long-running queries are completed; 0 to disable.
81
87
  beep_after_seconds = 0
82
88
 
@@ -831,6 +831,21 @@ def suggest_special(text: str) -> list[dict[str, Any]]:
831
831
  if cmd in ["\\llm", "/llm", "\\ai", "/ai"]:
832
832
  return [{"type": "llm"}]
833
833
 
834
+ if cmd.lower() in (r'\config', '/config'):
835
+ config_arguments = _arg.split(maxsplit=1)
836
+ config_subcommands = ['help', 'get', 'search', 'edit']
837
+ if config_arguments and config_arguments[0].lower() == 'get':
838
+ completing_property = (len(config_arguments) == 1 and text[-1].isspace()) or (
839
+ len(config_arguments) == 2 and not text[-1].isspace()
840
+ )
841
+ if completing_property:
842
+ prefix = config_arguments[1] if len(config_arguments) == 2 else ''
843
+ return [{'type': 'config_property', 'prefix': prefix}]
844
+ return []
845
+ if config_arguments and config_arguments[0].lower() in config_subcommands:
846
+ return []
847
+ return [{'type': 'special_subcommand', 'subcommands': config_subcommands}]
848
+
834
849
  if cmd.lower() in (r'\dsn', '/dsn'):
835
850
  dsn_arguments = _arg.split(maxsplit=1)
836
851
  completing_delete_target = (len(dsn_arguments) == 1 and text[-1].isspace()) or (len(dsn_arguments) == 2 and not text[-1].isspace())
@@ -81,6 +81,7 @@ from mycli.packages.special.main import (
81
81
  CommandNotFound,
82
82
  SpecialCommandAlias,
83
83
  execute,
84
+ is_special_command,
84
85
  parse_special_command,
85
86
  register_special_command,
86
87
  special_command,
@@ -110,6 +111,7 @@ __all__: list[str] = [
110
111
  'is_llm_command',
111
112
  'is_pager_enabled',
112
113
  'is_redirected',
114
+ 'is_special_command',
113
115
  'is_show_warnings_enabled',
114
116
  'is_timing_enabled',
115
117
  'list_databases',
@@ -1,12 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import os
4
5
  from typing import TYPE_CHECKING, Any
5
6
  from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
6
7
 
7
- from mycli.config import read_config_file, str_to_bool
8
+ from mycli.config import log, read_config_file, str_to_bool
8
9
  from mycli.constants import DEFAULT_CHARSET, DEFAULT_PROMPT, KNOWN_DSN_QUERY_PARAMS
9
10
 
11
+ logger = logging.getLogger(__name__)
12
+
10
13
  if TYPE_CHECKING:
11
14
  from mycli.client import MyCli
12
15
 
@@ -94,8 +97,44 @@ Examples:
94
97
  self.config_file = config_file
95
98
 
96
99
  @classmethod
97
- def from_config(cls, config: Any, mycli: MyCli | None = None, config_file: str | None = None) -> DsnAliases:
98
- return DsnAliases(config, mycli, config_file)
100
+ def from_config(
101
+ cls,
102
+ config: Any,
103
+ mycli: MyCli | None = None,
104
+ config_file: str | None = None,
105
+ shared_dsns_file: str | None = None,
106
+ ) -> DsnAliases:
107
+ aliases = cls(config, mycli, config_file)
108
+ if not shared_dsns_file:
109
+ return aliases
110
+
111
+ shared_dsns_file = os.path.expanduser(shared_dsns_file)
112
+ if not os.path.isabs(shared_dsns_file):
113
+ log(
114
+ logger,
115
+ logging.WARNING,
116
+ f"Shared DSNs file path must be absolute: '{shared_dsns_file}'.",
117
+ )
118
+ return aliases
119
+
120
+ if not os.path.isfile(shared_dsns_file):
121
+ log(
122
+ logger,
123
+ logging.WARNING,
124
+ f"Unable to read shared DSNs file '{shared_dsns_file}'.",
125
+ )
126
+ return aliases
127
+
128
+ shared_config = read_config_file(shared_dsns_file)
129
+ if shared_config is None:
130
+ return aliases
131
+
132
+ configured_aliases = config.get(cls.section_name, {})
133
+ shared_aliases = shared_config.get(cls.section_name, {})
134
+ config[cls.section_name] = {}
135
+ config[cls.section_name].update(shared_aliases)
136
+ config[cls.section_name].update(configured_aliases)
137
+ return aliases
99
138
 
100
139
  def _config_for_write(self) -> Any:
101
140
  if self.config_file is None:
@@ -72,6 +72,11 @@ def parse_special_command(sql: str) -> tuple[str, CommandVerbosity, str]:
72
72
  return (command, command_verbosity, arg.strip())
73
73
 
74
74
 
75
+ def is_special_command(sql: str) -> bool:
76
+ command, _, _ = parse_special_command(sql)
77
+ return command in CASE_SENSITIVE_COMMANDS or command.lower() in CASE_INSENSITIVE_COMMANDS
78
+
79
+
75
80
  def special_command(
76
81
  command: str,
77
82
  usage: str,
@@ -190,7 +195,7 @@ def execute(cur: Cursor, sql: str) -> list[SQLResult]:
190
195
  """
191
196
  command, command_verbosity, arg = parse_special_command(sql)
192
197
 
193
- if (command not in CASE_SENSITIVE_COMMANDS) and (command.lower() not in CASE_INSENSITIVE_COMMANDS):
198
+ if not is_special_command(sql):
194
199
  raise CommandNotFound(f'Command not found: {command}')
195
200
 
196
201
  try:
@@ -945,10 +945,12 @@ class SQLCompleter(Completer):
945
945
  supported_formats: tuple = (),
946
946
  keyword_casing: str = "auto",
947
947
  indexed_column_suffix: str = '*',
948
+ config_property_names: Collection[str] = (),
948
949
  ) -> None:
949
950
  super(self.__class__, self).__init__()
950
951
  self.smart_completion = smart_completion
951
952
  self.indexed_column_suffix = indexed_column_suffix
953
+ self.config_property_names = tuple(sorted(config_property_names))
952
954
  self.reserved_words = set()
953
955
  for x in self.keywords:
954
956
  self.reserved_words.update(x.split())
@@ -1452,6 +1454,8 @@ class SQLCompleter(Completer):
1452
1454
  suggestions = suggest_type(document.text, document.text_before_cursor)
1453
1455
  rigid_sort = False
1454
1456
  length_based_on_path = False
1457
+ config_property_length: int | None = None
1458
+ completion_filter_text = text_for_len
1455
1459
 
1456
1460
  rank = 0
1457
1461
  for suggestion in suggestions:
@@ -1745,6 +1749,13 @@ class SQLCompleter(Completer):
1745
1749
  )
1746
1750
  completions.extend([(*x, rank) for x in subcommands_m])
1747
1751
 
1752
+ elif suggestion['type'] == 'config_property':
1753
+ prefix = suggestion['prefix']
1754
+ property_matches = self.find_fuzzy_matches(prefix, prefix.lower(), self.config_property_names)
1755
+ completions.extend([(*x, rank) for x in property_matches])
1756
+ config_property_length = len(prefix)
1757
+ completion_filter_text = prefix.lower()
1758
+
1748
1759
  elif suggestion['type'] == 'dsn_alias':
1749
1760
  if hasattr(DsnAliases, 'instance'):
1750
1761
  aliases_m = self.find_matches(
@@ -1789,10 +1800,12 @@ class SQLCompleter(Completer):
1789
1800
  if rigid_sort:
1790
1801
  uniq_completions_str = dict.fromkeys(x[0] for x in completions)
1791
1802
  else:
1792
- sorted_completions = sorted(completions, key=lambda item: completion_sort_key(item, text_for_len.lower()))
1803
+ sorted_completions = sorted(completions, key=lambda item: completion_sort_key(item, completion_filter_text))
1793
1804
  uniq_completions_str = dict.fromkeys(x[0] for x in sorted_completions)
1794
1805
 
1795
- if length_based_on_path:
1806
+ if config_property_length is not None:
1807
+ return (Completion(x, -config_property_length) for x in uniq_completions_str)
1808
+ elif length_based_on_path:
1796
1809
  return (
1797
1810
  Completion(
1798
1811
  x,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.11.0
3
+ Version: 2.12.0
4
4
  Summary: CLI for MySQL Database. With auto-completion and syntax highlighting.
5
5
  Author: Mycli Core Team
6
6
  License-Expression: BSD-3-Clause
@@ -28,7 +28,7 @@ Requires-Python: >=3.10
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE.txt
30
30
  License-File: AUTHORS.rst
31
- Requires-Dist: click~=8.4.2
31
+ Requires-Dist: click~=8.3.3
32
32
  Requires-Dist: clickdc~=0.1.1
33
33
  Requires-Dist: cryptography~=50.0.0
34
34
  Requires-Dist: Pygments~=2.20.0
@@ -147,14 +147,14 @@ Features
147
147
  * Pretty print tabular data (with colors!).
148
148
  * Support for SSL connections
149
149
  * Shell-style trailing redirects with `$>`, `$>>` and `$|` operators.
150
- * [Polars](https://pola.rs) dataframe [transforms and plots](doc/transforms.md) with `.|`, and Parquet saves with `.>`.
151
- * Support for querying LLMs with context derived from your schema using `/llm`.
150
+ * [Polars](https://pola.rs) dataframe [transforms and plots](https://github.com/dbcli/mycli/blob/main/doc/transforms.md) with `.|`, and Parquet saves with `.>`.
151
+ * Support for [querying LLMs](https://www.mycli.net/llm) with context derived from your schema using `/llm`.
152
152
  * Support for storing passwords in the system keyring.
153
153
 
154
154
  Mycli creates a config file `~/.myclirc` on the first run; you can use the
155
155
  options in that file to configure the above features, and more.
156
156
 
157
- Some features are only exposed as [key bindings](doc/key_bindings.rst).
157
+ Some features are only exposed as [key bindings](https://github.com/dbcli/mycli/blob/main/doc/key_bindings.rst).
158
158
 
159
159
 
160
160
  Implementation
@@ -1,4 +1,4 @@
1
- click~=8.4.2
1
+ click~=8.3.3
2
2
  clickdc~=0.1.1
3
3
  cryptography~=50.0.0
4
4
  Pygments~=2.20.0
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.12.0",
3
+ "distance": 0,
4
+ "node": "g7b23738d63a6c2fa1ae25a598f3a7d2868c9d2b0",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-08"
8
+ }
@@ -25,7 +25,9 @@ classifiers = [
25
25
  ]
26
26
 
27
27
  dependencies = [
28
- "click ~= 8.4.2",
28
+ # click 8.4.2 breaks the pager on Windows; check the next
29
+ # version for https://github.com/pallets/click/pull/3739
30
+ "click ~= 8.3.3",
29
31
  "clickdc ~= 0.1.1",
30
32
  "cryptography ~= 50.0.0",
31
33
  "Pygments ~= 2.20.0",
@@ -10,7 +10,7 @@ Feature: run the cli,
10
10
 
11
11
  Scenario: run source command
12
12
  When we send source command
13
- then we see help output
13
+ then we see one
14
14
 
15
15
  Scenario: check our application_name
16
16
  When we run query to check application_name