batrachian-toad 0.5.0a8__tar.gz → 0.5.8__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 (152) hide show
  1. batrachian_toad-0.5.8/CHANGELOG.md +50 -0
  2. batrachian_toad-0.5.8/CONTRIBUTING.md +8 -0
  3. batrachian_toad-0.5.8/PKG-INFO +167 -0
  4. batrachian_toad-0.5.8/README.md +144 -0
  5. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/pyproject.toml +3 -3
  6. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/agent.py +32 -6
  7. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/messages.py +6 -1
  8. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/agent.py +3 -0
  9. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_ansi.py +1 -1
  10. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_stream_parser.py +1 -1
  11. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/app.py +47 -19
  12. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/cli.py +40 -24
  13. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/danger.py +2 -2
  14. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/claude.com.toml +1 -1
  15. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/inference.huggingface.co.toml +1 -1
  16. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/openhands.dev.toml +1 -1
  17. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/directory.py +16 -35
  18. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/fuzzy.py +1 -4
  19. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/jsonrpc.py +1 -1
  20. batrachian_toad-0.5.8/src/toad/path_filter.py +119 -0
  21. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/paths.py +1 -1
  22. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/prompt/extract.py +1 -1
  23. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/agent_modal.py +20 -2
  24. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/main.py +3 -3
  25. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/settings.py +3 -1
  26. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/store.py +54 -18
  27. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/store.tcss +25 -2
  28. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/settings_schema.py +35 -21
  29. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/toad.tcss +61 -19
  30. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/condensed_path.py +17 -6
  31. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/conversation.py +36 -7
  32. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/flash.py +1 -2
  33. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/grid_select.py +26 -11
  34. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/highlighted_textarea.py +1 -1
  35. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/path_search.py +34 -36
  36. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/prompt.py +33 -7
  37. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/question.py +11 -3
  38. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/tool_call.py +1 -1
  39. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/uv.lock +26 -112
  40. batrachian_toad-0.5.0a8/PKG-INFO +0 -123
  41. batrachian_toad-0.5.0a8/README.md +0 -100
  42. batrachian_toad-0.5.0a8/ansi_mandel.py +0 -87
  43. batrachian_toad-0.5.0a8/ansi_test.py +0 -15
  44. batrachian_toad-0.5.0a8/box_test.py +0 -4
  45. batrachian_toad-0.5.0a8/cpr.py +0 -39
  46. batrachian_toad-0.5.0a8/cursor_test.py +0 -8
  47. batrachian_toad-0.5.0a8/difffail.jsonl +0 -9
  48. batrachian_toad-0.5.0a8/mandel.py +0 -32
  49. batrachian_toad-0.5.0a8/mandelbrot.py +0 -51
  50. batrachian_toad-0.5.0a8/scroll_test.py +0 -6
  51. batrachian_toad-0.5.0a8/simple_test.py +0 -5
  52. batrachian_toad-0.5.0a8/table_movie.py +0 -197
  53. batrachian_toad-0.5.0a8/terminal_test.py +0 -297
  54. batrachian_toad-0.5.0a8/test_cursor_toggle.py +0 -7
  55. batrachian_toad-0.5.0a8/test_flow.py +0 -1
  56. batrachian_toad-0.5.0a8/test_ind.py +0 -48
  57. batrachian_toad-0.5.0a8/test_input.py +0 -2
  58. batrachian_toad-0.5.0a8/test_pygments.py +0 -18
  59. batrachian_toad-0.5.0a8/test_scroll.py +0 -5
  60. batrachian_toad-0.5.0a8/test_scroll1.py +0 -309
  61. batrachian_toad-0.5.0a8/test_scroll2.py +0 -82
  62. batrachian_toad-0.5.0a8/test_scroll_margins.py +0 -45
  63. batrachian_toad-0.5.0a8/test_scroll_region.py +0 -10
  64. batrachian_toad-0.5.0a8/test_size.py +0 -5
  65. batrachian_toad-0.5.0a8/tool.jsonl +0 -26
  66. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/.gitignore +0 -0
  67. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/.python-version +0 -0
  68. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/LICENSE +0 -0
  69. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/Makefile +0 -0
  70. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/notes.md +0 -0
  71. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/project/calculator.py +0 -0
  72. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/project/calculator.tcss +0 -0
  73. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/__init__.py +0 -0
  74. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/__main__.py +0 -0
  75. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/_loop.py +0 -0
  76. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/about.py +0 -0
  77. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/api.py +0 -0
  78. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/encode_tool_call_id.py +0 -0
  79. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/prompt.py +0 -0
  80. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/acp/protocol.py +0 -0
  81. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/agent_schema.py +0 -0
  82. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/agents.py +0 -0
  83. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/__init__.py +0 -0
  84. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_ansi_colors.py +0 -0
  85. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_control_codes.py +0 -0
  86. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_keys.py +0 -0
  87. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/ansi/_sgr_styles.py +0 -0
  88. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/answer.py +0 -0
  89. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/atomic.py +0 -0
  90. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/code_analyze.py +0 -0
  91. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/complete.py +0 -0
  92. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/constants.py +0 -0
  93. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/conversation_markdown.py +0 -0
  94. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/ampcode.com.toml +0 -0
  95. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/augmentcode.com.toml +0 -0
  96. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/docker.com.toml +0 -0
  97. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/geminicli.com.toml +0 -0
  98. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/goose.ai.toml +0 -0
  99. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/kimi.com.toml +0 -0
  100. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/openai.com.toml +0 -0
  101. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/opencode.ai.toml +0 -0
  102. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/stakpak.dev.toml +0 -0
  103. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/vibe.mistral.ai.toml +0 -0
  104. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/data/agents/vtcode.dev.toml +0 -0
  105. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/db.py +0 -0
  106. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/dec.py +0 -0
  107. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/gist.py +0 -0
  108. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/history.py +0 -0
  109. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/menus.py +0 -0
  110. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/messages.py +0 -0
  111. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/option_content.py +0 -0
  112. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/os.py +0 -0
  113. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/path_complete.py +0 -0
  114. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/pill.py +0 -0
  115. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/prompt/resource.py +0 -0
  116. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/protocol.py +0 -0
  117. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/action_modal.py +0 -0
  118. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/permissions.py +0 -0
  119. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/permissions.tcss +0 -0
  120. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/screens/settings.tcss +0 -0
  121. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/settings.py +0 -0
  122. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/shell.py +0 -0
  123. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/shell_read.py +0 -0
  124. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/slash_command.py +0 -0
  125. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/version.py +0 -0
  126. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/visuals/columns.py +0 -0
  127. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/agent_response.py +0 -0
  128. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/agent_thought.py +0 -0
  129. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/command_pane.py +0 -0
  130. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/danger_warning.py +0 -0
  131. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/diff_view.py +0 -0
  132. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/future_text.py +0 -0
  133. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/mandelbrot.py +0 -0
  134. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/markdown_note.py +0 -0
  135. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/menu.py +0 -0
  136. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/non_selectable_label.py +0 -0
  137. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/note.py +0 -0
  138. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/plan.py +0 -0
  139. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/project_directory_tree.py +0 -0
  140. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/shell_result.py +0 -0
  141. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/shell_terminal.py +0 -0
  142. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/side_bar.py +0 -0
  143. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/slash_complete.py +0 -0
  144. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/strike_text.py +0 -0
  145. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/terminal.py +0 -0
  146. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/terminal_tool.py +0 -0
  147. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/throbber.py +0 -0
  148. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/user_input.py +0 -0
  149. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/version.py +0 -0
  150. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/src/toad/widgets/welcome.py +0 -0
  151. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8/tools}/echo_client.py +0 -0
  152. {batrachian_toad-0.5.0a8 → batrachian_toad-0.5.8}/tools/make_qr.py +0 -0
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.5.8] - 2025-12-26
9
+
10
+ ### Fixed
11
+
12
+ - Fixed broken tool calls
13
+
14
+ ## [0.5.7] - 2025-12-26
15
+
16
+ ### Changes
17
+
18
+ - Cursor keys can navigate between sections in the store screen
19
+ - Optimized path search
20
+ - Disabled path search in shell mode
21
+ - Typing in the conversation view will auto-focus the prompt
22
+
23
+ ### Added
24
+
25
+ - Added single character switches https://github.com/batrachianai/toad/pull/135
26
+
27
+ ## [0.5.6] - 2025-12-24
28
+
29
+ ### Fixed
30
+
31
+ - Fixed agent selector not focusing on run.
32
+ - Added project directory as second argument to `toad acp` rather than a switch.
33
+
34
+ ## [0.5.5] - 2025-12-22
35
+
36
+ ### Fixed
37
+
38
+ - Fixed column setting not taking effect
39
+
40
+ ## [0.5.0] - 2025-12-18
41
+
42
+ ### Added
43
+
44
+ - First release. This document will be updated for subsequent releases.
45
+
46
+ [0.5.8]: https://github.com/Textualize/textual/compare/v0.5.7...v0.5.8
47
+ [0.5.7]: https://github.com/Textualize/textual/compare/v0.5.6...v0.5.7
48
+ [0.5.6]: https://github.com/Textualize/textual/compare/v0.5.5...v0.5.6
49
+ [0.5.5]: https://github.com/Textualize/textual/compare/v0.5.0...v0.5.5
50
+ [0.5.0]: https://github.com/batrachianai/toad/releases/tag/v0.5.0
@@ -0,0 +1,8 @@
1
+ # Guide to Contributing
2
+
3
+ Thank you for your interesting in improving Toad!
4
+
5
+ If you are thinking of fixing a bug or contributing a feature, please open a Discussion first.
6
+ You will be asked for a link to the discussion when you contribute a PR.
7
+
8
+ TODO: add technical help
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: batrachian-toad
3
+ Version: 0.5.8
4
+ Summary: A unified experience for AI in your terminal.
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.14
7
+ Requires-Dist: bashlex>=0.18
8
+ Requires-Dist: click>=8.2.1
9
+ Requires-Dist: gitpython>=3.1.44
10
+ Requires-Dist: google-re2>=1.1.20251105
11
+ Requires-Dist: httpx>=0.28.1
12
+ Requires-Dist: packaging>=25.0
13
+ Requires-Dist: pathspec>=0.12.1
14
+ Requires-Dist: platformdirs>=4.3.8
15
+ Requires-Dist: rich
16
+ Requires-Dist: textual-serve>=1.1.2
17
+ Requires-Dist: textual-speedups==0.2.1
18
+ Requires-Dist: textual[syntax]>=6.11.0
19
+ Requires-Dist: tree-sitter>=0.24.0
20
+ Requires-Dist: typeguard>=4.4.4
21
+ Requires-Dist: xdg-base-dirs>=6.0.2
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Toad
25
+
26
+ A unified interface for AI in your terminal ([release announcement](https://willmcgugan.github.io/toad-released/)).
27
+
28
+ Run coding agents seamlessly under a single beautiful terminal UI, thanks to the [ACP](https://agentclientprotocol.com/protocol/initialization) protocol.
29
+
30
+ <table>
31
+
32
+ <tbody>
33
+
34
+ <tr>
35
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 58 58" src="https://github.com/user-attachments/assets/98387559-2e10-485a-8a7d-82cb00ed7622" /></td>
36
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 04" src="https://github.com/user-attachments/assets/d4231320-b678-47ba-99ce-02746ca2622b" /></td>
37
+ </tr>
38
+
39
+ <tr>
40
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 22" src="https://github.com/user-attachments/assets/ddba550d-ff33-45ad-9f93-281187f5c974" /></td>
41
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 37" src="https://github.com/user-attachments/assets/e7943272-39a5-40a1-bedf-e440002e1290" /></td>
42
+ </tr>
43
+
44
+ </tbody>
45
+
46
+
47
+ </table>
48
+
49
+ ## Compatibility
50
+
51
+ Toad runs on Linux and macOS. Native Windows support is currently lacking (but on the roadmap), but Toad will run quite well with WSL.
52
+
53
+ Toad is a terminal application.
54
+ Any terminal will work, although if you are using the default terminal on macOS you will get a much reduced experience.
55
+ I recommend [Ghostty](https://ghostty.org/) which is fully featured and has amazing performance.
56
+
57
+
58
+ ## Getting Started
59
+
60
+ The easiest way to install Toad is by pasting the following in to your terminal:
61
+
62
+ ```bash
63
+ curl -fsSL batrachian.ai/install | sh
64
+ ```
65
+
66
+ You should now have `toad` installed.
67
+
68
+ If that doesn't work for any reason, then you can install with the following steps:
69
+
70
+ First [install UV](https://docs.astral.sh/uv/getting-started/installation/):
71
+
72
+ ```bash
73
+ curl -LsSf https://astral.sh/uv/install.sh | sh
74
+ ```
75
+
76
+ Then use UV to install toad:
77
+
78
+ ```bash
79
+ uv tool install -U batrachian-toad --python 3.14
80
+ ```
81
+
82
+ ## Using Toad
83
+
84
+ Launch Toad with the following:
85
+
86
+ ```bash
87
+ toad
88
+ ```
89
+
90
+ You should see something like this:
91
+
92
+ <img width="1266" height="994" alt="front-fs8" src="https://github.com/user-attachments/assets/8831f7de-5349-4b3f-9de9-d4565b513108" />
93
+
94
+ From this screen you will be able to find, install, and launch a coding agent.
95
+ If you already have an agent installed, you can skip the install step.
96
+ To launch an agent, select it and press space.
97
+
98
+ The footer will always display the most significant keys for the current context.
99
+ To see all the keys, summon the command palette with `ctrl+p` and search for "keys".
100
+
101
+ ### Toad CLI
102
+
103
+ When running Toad, the current working directory is assumed to be your project directory.
104
+ To use another project directory, add the path to the command.
105
+ For example:
106
+
107
+ ```bash
108
+ toad ~/projects/my-awesome-app
109
+ ```
110
+
111
+ If you want to skip the initial agent screen, add the `-a` switch with the name of your chosen agent.
112
+ For example:
113
+
114
+ ```bash
115
+ toad -a open-hands
116
+ ```
117
+
118
+ To see all subcommands and switches, add the `--help` switch:
119
+
120
+ ```bash
121
+ toad --help
122
+ ```
123
+
124
+ ### Web server
125
+
126
+ You can run Toad as a web application.
127
+
128
+ Run the following, and click the link in the terminal:
129
+
130
+ ```bash
131
+ toad serve
132
+ ```
133
+
134
+ ![textual-serve](https://github.com/user-attachments/assets/1d861d48-d30b-44cd-972d-5986a01360bf)
135
+
136
+ ## Toad development
137
+
138
+ Toad was built by [Will McGugan](https://github.com/willmcgugan) and is currently under active development.
139
+
140
+ To discuss Toad, see the Discussions tab, or join the #toad channel on the [Textualize discord server](https://discord.gg/Enf6Z3qhVr).
141
+
142
+ ### Roadmap
143
+
144
+ Some planned features:
145
+
146
+ - UI for MCP servers
147
+ - Expose model selection (waiting on ACP update)
148
+ - Sessions
149
+ - Multiple agents
150
+ - Windows native support
151
+
152
+ ### Reporting bugs
153
+
154
+ This project is trialling a non-traditional approach to issues.
155
+ Before an issue is created, there must be a post in Dicussions, approved by a Toad dev (Currently @willmcgugan).
156
+
157
+ By allowing the discussions to happen in the Discussion tabs, issues can be reserved for actionable tasks with a clear description and goal.
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+
167
+
@@ -0,0 +1,144 @@
1
+ # Toad
2
+
3
+ A unified interface for AI in your terminal ([release announcement](https://willmcgugan.github.io/toad-released/)).
4
+
5
+ Run coding agents seamlessly under a single beautiful terminal UI, thanks to the [ACP](https://agentclientprotocol.com/protocol/initialization) protocol.
6
+
7
+ <table>
8
+
9
+ <tbody>
10
+
11
+ <tr>
12
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 58 58" src="https://github.com/user-attachments/assets/98387559-2e10-485a-8a7d-82cb00ed7622" /></td>
13
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 04" src="https://github.com/user-attachments/assets/d4231320-b678-47ba-99ce-02746ca2622b" /></td>
14
+ </tr>
15
+
16
+ <tr>
17
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 22" src="https://github.com/user-attachments/assets/ddba550d-ff33-45ad-9f93-281187f5c974" /></td>
18
+ <td><img width="1338" height="1004" alt="Screenshot 2025-10-23 at 08 59 37" src="https://github.com/user-attachments/assets/e7943272-39a5-40a1-bedf-e440002e1290" /></td>
19
+ </tr>
20
+
21
+ </tbody>
22
+
23
+
24
+ </table>
25
+
26
+ ## Compatibility
27
+
28
+ Toad runs on Linux and macOS. Native Windows support is currently lacking (but on the roadmap), but Toad will run quite well with WSL.
29
+
30
+ Toad is a terminal application.
31
+ Any terminal will work, although if you are using the default terminal on macOS you will get a much reduced experience.
32
+ I recommend [Ghostty](https://ghostty.org/) which is fully featured and has amazing performance.
33
+
34
+
35
+ ## Getting Started
36
+
37
+ The easiest way to install Toad is by pasting the following in to your terminal:
38
+
39
+ ```bash
40
+ curl -fsSL batrachian.ai/install | sh
41
+ ```
42
+
43
+ You should now have `toad` installed.
44
+
45
+ If that doesn't work for any reason, then you can install with the following steps:
46
+
47
+ First [install UV](https://docs.astral.sh/uv/getting-started/installation/):
48
+
49
+ ```bash
50
+ curl -LsSf https://astral.sh/uv/install.sh | sh
51
+ ```
52
+
53
+ Then use UV to install toad:
54
+
55
+ ```bash
56
+ uv tool install -U batrachian-toad --python 3.14
57
+ ```
58
+
59
+ ## Using Toad
60
+
61
+ Launch Toad with the following:
62
+
63
+ ```bash
64
+ toad
65
+ ```
66
+
67
+ You should see something like this:
68
+
69
+ <img width="1266" height="994" alt="front-fs8" src="https://github.com/user-attachments/assets/8831f7de-5349-4b3f-9de9-d4565b513108" />
70
+
71
+ From this screen you will be able to find, install, and launch a coding agent.
72
+ If you already have an agent installed, you can skip the install step.
73
+ To launch an agent, select it and press space.
74
+
75
+ The footer will always display the most significant keys for the current context.
76
+ To see all the keys, summon the command palette with `ctrl+p` and search for "keys".
77
+
78
+ ### Toad CLI
79
+
80
+ When running Toad, the current working directory is assumed to be your project directory.
81
+ To use another project directory, add the path to the command.
82
+ For example:
83
+
84
+ ```bash
85
+ toad ~/projects/my-awesome-app
86
+ ```
87
+
88
+ If you want to skip the initial agent screen, add the `-a` switch with the name of your chosen agent.
89
+ For example:
90
+
91
+ ```bash
92
+ toad -a open-hands
93
+ ```
94
+
95
+ To see all subcommands and switches, add the `--help` switch:
96
+
97
+ ```bash
98
+ toad --help
99
+ ```
100
+
101
+ ### Web server
102
+
103
+ You can run Toad as a web application.
104
+
105
+ Run the following, and click the link in the terminal:
106
+
107
+ ```bash
108
+ toad serve
109
+ ```
110
+
111
+ ![textual-serve](https://github.com/user-attachments/assets/1d861d48-d30b-44cd-972d-5986a01360bf)
112
+
113
+ ## Toad development
114
+
115
+ Toad was built by [Will McGugan](https://github.com/willmcgugan) and is currently under active development.
116
+
117
+ To discuss Toad, see the Discussions tab, or join the #toad channel on the [Textualize discord server](https://discord.gg/Enf6Z3qhVr).
118
+
119
+ ### Roadmap
120
+
121
+ Some planned features:
122
+
123
+ - UI for MCP servers
124
+ - Expose model selection (waiting on ACP update)
125
+ - Sessions
126
+ - Multiple agents
127
+ - Windows native support
128
+
129
+ ### Reporting bugs
130
+
131
+ This project is trialling a non-traditional approach to issues.
132
+ Before an issue is created, there must be a post in Dicussions, approved by a Toad dev (Currently @willmcgugan).
133
+
134
+ By allowing the discussions to happen in the Discussion tabs, issues can be reserved for actionable tasks with a clear description and goal.
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "batrachian-toad"
3
- version = "0.5.0a8"
3
+ version = "0.5.8"
4
4
  description = "A unified experience for AI in your terminal."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
7
7
  dependencies = [
8
- "textual[syntax]>=6.10.0",
8
+ "textual[syntax]>=6.11.0",
9
9
  "click>=8.2.1",
10
10
  "gitpython>=3.1.44",
11
11
  "tree-sitter>=0.24.0",
@@ -17,9 +17,9 @@ dependencies = [
17
17
  "textual-serve>=1.1.2",
18
18
  "textual-speedups==0.2.1",
19
19
  "packaging>=25.0",
20
- "posthog>=7.0.1",
21
20
  "bashlex>=0.18",
22
21
  "pathspec>=0.12.1",
22
+ "google-re2>=1.1.20251105",
23
23
  ]
24
24
 
25
25
  [tool.uv.workspace]
@@ -3,7 +3,7 @@ import asyncio
3
3
  import json
4
4
  import os
5
5
  from pathlib import Path
6
- from typing import cast, NamedTuple
6
+ from typing import Any, cast, NamedTuple
7
7
  from copy import deepcopy
8
8
 
9
9
  import rich.repr
@@ -125,11 +125,22 @@ class Agent(AgentBase):
125
125
  return message_target.post_message(message)
126
126
 
127
127
  @jsonrpc.expose("session/update")
128
- def rpc_session_update(self, sessionId: str, update: protocol.SessionUpdate):
128
+ def rpc_session_update(
129
+ self,
130
+ sessionId: str,
131
+ update: protocol.SessionUpdate,
132
+ _meta: dict[str, Any] | None = None,
133
+ ):
129
134
  """Agent requests an update.
130
135
 
131
136
  https://agentclientprotocol.com/protocol/schema
132
137
  """
138
+ status_line: str | None = None
139
+ if _meta and (field_meta := _meta.get("field_meta")) is not None:
140
+ if (
141
+ open_hands_metrics := field_meta.get("openhands.dev/metrics")
142
+ ) is not None:
143
+ status_line = open_hands_metrics.get("status_line")
133
144
 
134
145
  match update:
135
146
  case {
@@ -190,6 +201,9 @@ class Agent(AgentBase):
190
201
  case {"sessionUpdate": "current_mode_update", "currentModeId": mode_id}:
191
202
  self.post_message(messages.ModeUpdate(mode_id))
192
203
 
204
+ if status_line is not None:
205
+ self.post_message(messages.UpdateStatusLine(status_line))
206
+
193
207
  @jsonrpc.expose("session/request_permission")
194
208
  async def rpc_request_permission(
195
209
  self,
@@ -359,7 +373,10 @@ class Agent(AgentBase):
359
373
  async def _run_agent(self) -> None:
360
374
  """Task to communicate with the agent subprocess."""
361
375
 
362
- agent_output = open("agent.jsonl", "wb")
376
+ if constants.DEBUG:
377
+ agent_output = open("agent.jsonl", "wb")
378
+ else:
379
+ agent_output = None
363
380
 
364
381
  PIPE = asyncio.subprocess.PIPE
365
382
  env = os.environ.copy()
@@ -411,8 +428,9 @@ class Agent(AgentBase):
411
428
  if not line.strip():
412
429
  continue
413
430
 
414
- agent_output.write(line)
415
- agent_output.flush()
431
+ if agent_output is not None:
432
+ agent_output.write(line)
433
+ agent_output.flush()
416
434
 
417
435
  try:
418
436
  line_str = line.decode("utf-8")
@@ -464,7 +482,15 @@ class Agent(AgentBase):
464
482
  )
465
483
  )
466
484
 
467
- agent_output.close()
485
+ if agent_output is not None:
486
+ agent_output.close()
487
+
488
+ self._process = None
489
+
490
+ async def stop(self) -> None:
491
+ """Gracefully stop the process."""
492
+ if self._process is not None:
493
+ self._process.terminate()
468
494
 
469
495
  async def run(self) -> None:
470
496
  """The main logic of the Agent."""
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
 
5
5
  from asyncio import Future
6
- from typing import Mapping, TYPE_CHECKING
6
+ from typing import Any, Mapping, TYPE_CHECKING
7
7
  from textual.message import Message
8
8
 
9
9
  import rich.repr
@@ -27,6 +27,11 @@ class Thinking(AgentMessage):
27
27
  text: str
28
28
 
29
29
 
30
+ @dataclass
31
+ class UpdateStatusLine(AgentMessage):
32
+ status_line: str
33
+
34
+
30
35
  @dataclass
31
36
  class Update(AgentMessage):
32
37
  type: str
@@ -57,3 +57,6 @@ class AgentBase(ABC):
57
57
 
58
58
  def get_info(self) -> Content:
59
59
  return Content("")
60
+
61
+ async def stop(self) -> None:
62
+ """Stop the agent (gracefully exit the process)"""
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import io
4
4
  from itertools import accumulate
5
- import re
5
+ import re2 as re
6
6
 
7
7
  from dataclasses import dataclass, field
8
8
  from functools import lru_cache
@@ -1,6 +1,6 @@
1
1
  from functools import lru_cache
2
2
  import io
3
- import re
3
+ import re2 as re
4
4
 
5
5
  import rich.repr
6
6
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ from datetime import datetime, timezone
2
3
  from functools import cached_property
3
4
  from pathlib import Path
4
5
  import json
@@ -7,15 +8,14 @@ from typing import Any, ClassVar, TYPE_CHECKING
7
8
 
8
9
  from rich import terminal_theme
9
10
 
10
- from textual import work
11
+ from textual import on, work
11
12
  from textual.binding import Binding, BindingType
12
13
  from textual.reactive import var, reactive
13
14
  from textual.app import App
15
+ from textual import events
14
16
  from textual.signal import Signal
15
17
 
16
18
 
17
- from posthog import Posthog
18
-
19
19
  from toad.settings import Schema, Settings
20
20
  from toad.agent_schema import Agent as AgentData
21
21
  from toad.settings_schema import SCHEMA
@@ -29,12 +29,6 @@ if TYPE_CHECKING:
29
29
  from toad.screens.store import StoreScreen
30
30
 
31
31
 
32
- import warnings
33
-
34
- # TODO: Look in to Posthog Syntax warnings
35
- warnings.filterwarnings("ignore", category=SyntaxWarning)
36
-
37
-
38
32
  DRACULA_TERMINAL_THEME = terminal_theme.TerminalTheme(
39
33
  background=(40, 42, 54), # #282A36
40
34
  foreground=(248, 248, 242), # #F8F8F2
@@ -217,6 +211,8 @@ def get_store_screen() -> StoreScreen:
217
211
 
218
212
 
219
213
  class ToadApp(App, inherit_bindings=False):
214
+ """The top level app."""
215
+
220
216
  SCREENS = {"settings": get_settings_screen}
221
217
  MODES = {"store": get_store_screen}
222
218
  BINDING_GROUP_TITLE = "System"
@@ -268,10 +264,7 @@ class ToadApp(App, inherit_bindings=False):
268
264
  )
269
265
  self._initial_mode = mode
270
266
  self.version_meta: VersionMeta | None = None
271
- self.posthog = Posthog(
272
- project_api_key="phc_mJWPV7GP3ar1i9vxBg2U8aiKsjNgVwum6F6ZggaD4ri",
273
- host="https://us.i.posthog.com",
274
- )
267
+
275
268
  super().__init__()
276
269
 
277
270
  @property
@@ -324,13 +317,30 @@ class ToadApp(App, inherit_bindings=False):
324
317
  if not self.settings.get("statistics.allow_collect", bool):
325
318
  # User has disabled stats
326
319
  return
320
+
321
+ POSTHOG_API_KEY = "phc_mJWPV7GP3ar1i9vxBg2U8aiKsjNgVwum6F6ZggaD4ri"
322
+ POSTHOG_HOST = "https://us.i.posthog.com"
323
+ POSTHOG_EVENT_URL = f"{POSTHOG_HOST}/i/v0/e/"
324
+
325
+ import platform
326
+ import httpx
327
+
328
+ timestamp = datetime.now(timezone.utc).isoformat()
329
+
327
330
  event_properties = {"toad_version": self.version} | properties
328
- await asyncio.to_thread(
329
- self.posthog.capture,
330
- event_name,
331
- distinct_id=self.anon_id,
332
- properties=event_properties,
333
- )
331
+ body_json = {
332
+ "api_key": POSTHOG_API_KEY,
333
+ "event": event_name,
334
+ "distinct_id": self.anon_id,
335
+ "properties": event_properties,
336
+ "timestamp": timestamp,
337
+ "os": platform.system(),
338
+ }
339
+ try:
340
+ async with httpx.AsyncClient() as client:
341
+ await client.post(POSTHOG_EVENT_URL, json=body_json)
342
+ except Exception:
343
+ pass
334
344
 
335
345
  def save_settings(self) -> None:
336
346
  if self.settings.changed:
@@ -355,8 +365,16 @@ class ToadApp(App, inherit_bindings=False):
355
365
  elif key == "ui.scrollbar":
356
366
  if isinstance(value, str):
357
367
  self.scrollbar = value
368
+ elif key == "ui.compact-input":
369
+ self.set_class(bool(value), "-compact-input")
358
370
  elif key == "ui.footer":
359
371
  self.set_class(not bool(value), "-hide-footer")
372
+ elif key == "ui.status-line":
373
+ self.set_class(not bool(value), "-hide-status-line")
374
+ elif key == "ui.agent-title":
375
+ self.set_class(not bool(value), "-hide-agent-title")
376
+ elif key == "ui.info-bar":
377
+ self.set_class(not bool(value), "-hide-info-bar")
360
378
  elif key == "agent.thoughts":
361
379
  self.set_class(not bool(value), "-hide-thoughts")
362
380
  elif key == "sidebar.hide":
@@ -387,6 +405,16 @@ class ToadApp(App, inherit_bindings=False):
387
405
 
388
406
  self.set_timer(1, self.run_version_check)
389
407
 
408
+ @on(events.TextSelected)
409
+ async def on_text_selected(self) -> None:
410
+ if self.settings.get("ui.auto_copy", bool):
411
+ if (selection := self.screen.get_selected_text()) is not None:
412
+ self.copy_to_clipboard(selection)
413
+ self.notify(
414
+ "Copied selection to clipboard (see settings)",
415
+ title="Automatic copy",
416
+ )
417
+
390
418
  def run_on_exit(self):
391
419
  if self.update_required and self.version_meta is not None:
392
420
  version_meta = self.version_meta