ripperdoc 0.2.10__tar.gz → 0.3.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. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/PKG-INFO +49 -17
  2. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/README.md +46 -15
  3. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/pyproject.toml +2 -1
  4. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/__init__.py +1 -1
  5. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/cli.py +164 -57
  6. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/__init__.py +4 -0
  7. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/agents_cmd.py +3 -7
  8. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/doctor_cmd.py +29 -0
  9. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/memory_cmd.py +2 -1
  10. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/models_cmd.py +61 -5
  11. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/resume_cmd.py +1 -0
  12. ripperdoc-0.3.0/ripperdoc/cli/commands/skills_cmd.py +103 -0
  13. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/stats_cmd.py +4 -4
  14. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/status_cmd.py +10 -0
  15. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/tasks_cmd.py +6 -3
  16. ripperdoc-0.3.0/ripperdoc/cli/commands/themes_cmd.py +139 -0
  17. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/file_mention_completer.py +63 -13
  18. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/helpers.py +6 -3
  19. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/interrupt_handler.py +34 -0
  20. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/panels.py +13 -8
  21. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/rich_ui.py +451 -32
  22. ripperdoc-0.3.0/ripperdoc/cli/ui/spinner.py +148 -0
  23. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/tool_renderers.py +10 -9
  24. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/wizard.py +18 -11
  25. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/agents.py +4 -0
  26. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/config.py +235 -0
  27. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/default_tools.py +1 -0
  28. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/llm_callback.py +0 -1
  29. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/manager.py +6 -0
  30. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/permissions.py +82 -5
  31. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/providers/openai.py +55 -9
  32. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/query.py +349 -108
  33. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/query_utils.py +17 -14
  34. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/skills.py +1 -0
  35. ripperdoc-0.3.0/ripperdoc/core/theme.py +298 -0
  36. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/tool.py +8 -3
  37. ripperdoc-0.3.0/ripperdoc/protocol/__init__.py +14 -0
  38. ripperdoc-0.3.0/ripperdoc/protocol/models.py +300 -0
  39. ripperdoc-0.3.0/ripperdoc/protocol/stdio.py +1453 -0
  40. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/background_shell.py +49 -5
  41. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/bash_tool.py +75 -9
  42. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/file_edit_tool.py +98 -29
  43. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/file_read_tool.py +139 -8
  44. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/file_write_tool.py +46 -3
  45. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/grep_tool.py +98 -8
  46. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/lsp_tool.py +9 -15
  47. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/multi_edit_tool.py +26 -3
  48. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/skill_tool.py +52 -1
  49. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/task_tool.py +33 -8
  50. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/file_watch.py +12 -6
  51. ripperdoc-0.3.0/ripperdoc/utils/image_utils.py +125 -0
  52. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/log.py +30 -3
  53. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/lsp.py +9 -3
  54. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/mcp.py +80 -18
  55. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/message_formatting.py +2 -2
  56. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/messages.py +177 -32
  57. ripperdoc-0.3.0/ripperdoc/utils/pending_messages.py +50 -0
  58. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/permissions/shell_command_validation.py +3 -3
  59. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/permissions/tool_permission_utils.py +9 -3
  60. ripperdoc-0.3.0/ripperdoc/utils/platform.py +198 -0
  61. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/session_heatmap.py +1 -3
  62. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/session_history.py +2 -2
  63. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/session_stats.py +1 -0
  64. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/shell_utils.py +8 -5
  65. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/todo.py +0 -6
  66. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/PKG-INFO +49 -17
  67. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/SOURCES.txt +19 -3
  68. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/requires.txt +2 -1
  69. ripperdoc-0.3.0/tests/test_background_notifications.py +57 -0
  70. ripperdoc-0.3.0/tests/test_background_shell_status.py +33 -0
  71. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_cli_commands.py +2 -0
  72. ripperdoc-0.3.0/tests/test_cli_stdin.py +43 -0
  73. ripperdoc-0.3.0/tests/test_file_edit_tool.py +705 -0
  74. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_file_mention_completer.py +3 -3
  75. ripperdoc-0.3.0/tests/test_git_utils.py +527 -0
  76. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_hooks.py +608 -396
  77. ripperdoc-0.3.0/tests/test_interrupt_handler.py +505 -0
  78. ripperdoc-0.3.0/tests/test_pending_messages.py +110 -0
  79. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_permissions.py +4 -4
  80. ripperdoc-0.3.0/tests/test_platform.py +362 -0
  81. ripperdoc-0.3.0/tests/test_rich_ui_suggestions.py +20 -0
  82. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_shell_permissions.py +6 -5
  83. ripperdoc-0.3.0/tests/test_shell_utils.py +423 -0
  84. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_utils.py +23 -23
  85. ripperdoc-0.2.10/ripperdoc/cli/ui/spinner.py +0 -85
  86. ripperdoc-0.2.10/ripperdoc/sdk/__init__.py +0 -9
  87. ripperdoc-0.2.10/ripperdoc/sdk/client.py +0 -408
  88. ripperdoc-0.2.10/tests/test_sdk.py +0 -46
  89. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/LICENSE +0 -0
  90. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/__main__.py +0 -0
  91. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/__init__.py +0 -0
  92. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/base.py +0 -0
  93. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/clear_cmd.py +0 -0
  94. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/compact_cmd.py +0 -0
  95. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/config_cmd.py +0 -0
  96. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/context_cmd.py +0 -0
  97. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/cost_cmd.py +0 -0
  98. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/exit_cmd.py +0 -0
  99. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/help_cmd.py +0 -0
  100. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/hooks_cmd.py +0 -0
  101. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/mcp_cmd.py +0 -0
  102. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/permissions_cmd.py +0 -0
  103. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/todos_cmd.py +0 -0
  104. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/commands/tools_cmd.py +0 -0
  105. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/__init__.py +0 -0
  106. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/context_display.py +0 -0
  107. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/message_display.py +0 -0
  108. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/provider_options.py +0 -0
  109. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/cli/ui/thinking_spinner.py +0 -0
  110. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/__init__.py +0 -0
  111. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/commands.py +0 -0
  112. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/custom_commands.py +0 -0
  113. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/__init__.py +0 -0
  114. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/config.py +0 -0
  115. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/events.py +0 -0
  116. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/executor.py +0 -0
  117. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/hooks/integration.py +0 -0
  118. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/providers/__init__.py +0 -0
  119. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/providers/anthropic.py +0 -0
  120. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/providers/base.py +0 -0
  121. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/providers/gemini.py +0 -0
  122. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/core/system_prompt.py +0 -0
  123. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/__init__.py +0 -0
  124. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/ask_user_question_tool.py +0 -0
  125. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/bash_output_tool.py +0 -0
  126. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/dynamic_mcp_tool.py +0 -0
  127. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/enter_plan_mode_tool.py +0 -0
  128. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/exit_plan_mode_tool.py +0 -0
  129. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/glob_tool.py +0 -0
  130. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/kill_bash_tool.py +0 -0
  131. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/ls_tool.py +0 -0
  132. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/mcp_tools.py +0 -0
  133. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/notebook_edit_tool.py +0 -0
  134. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/todo_tool.py +0 -0
  135. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/tools/tool_search_tool.py +0 -0
  136. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/__init__.py +0 -0
  137. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/bash_constants.py +0 -0
  138. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/bash_output_utils.py +0 -0
  139. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/coerce.py +0 -0
  140. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/context_length_errors.py +0 -0
  141. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/conversation_compaction.py +0 -0
  142. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/exit_code_handlers.py +0 -0
  143. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/git_utils.py +0 -0
  144. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/json_utils.py +0 -0
  145. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/memory.py +0 -0
  146. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/message_compaction.py +0 -0
  147. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/output_utils.py +0 -0
  148. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/path_ignore.py +0 -0
  149. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/path_utils.py +0 -0
  150. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/permissions/__init__.py +0 -0
  151. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/permissions/path_validation_utils.py +0 -0
  152. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/prompt.py +0 -0
  153. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/safe_get_cwd.py +0 -0
  154. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/sandbox_utils.py +0 -0
  155. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/session_usage.py +0 -0
  156. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/shell_token_utils.py +0 -0
  157. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc/utils/token_estimation.py +0 -0
  158. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/dependency_links.txt +0 -0
  159. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/entry_points.txt +0 -0
  160. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/ripperdoc.egg-info/top_level.txt +0 -0
  161. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/setup.cfg +0 -0
  162. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/setup.py +0 -0
  163. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_background_shell_shutdown.py +0 -0
  164. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_compact.py +0 -0
  165. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_config.py +0 -0
  166. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_context_length_errors.py +0 -0
  167. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_context_limits.py +0 -0
  168. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_custom_commands.py +0 -0
  169. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_hooks_cmd.py +0 -0
  170. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_mcp_config.py +0 -0
  171. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_messages.py +0 -0
  172. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_output_utils.py +0 -0
  173. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_path_ignore.py +0 -0
  174. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_query_abort.py +0 -0
  175. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_skills.py +0 -0
  176. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_todo.py +0 -0
  177. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_tool_search.py +0 -0
  178. {ripperdoc-0.2.10 → ripperdoc-0.3.0}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripperdoc
3
- Version: 0.2.10
3
+ Version: 0.3.0
4
4
  Summary: AI-powered terminal assistant for coding tasks
5
5
  Author: Ripperdoc Team
6
6
  License: Apache-2.0
@@ -23,10 +23,11 @@ Requires-Dist: python-dotenv>=1.0.0
23
23
  Requires-Dist: aiofiles>=23.0.0
24
24
  Requires-Dist: prompt-toolkit>=3.0.0
25
25
  Requires-Dist: PyYAML>=6.0.0
26
- Requires-Dist: mcp[cli]>=1.22.0
26
+ Requires-Dist: mcp[cli]>=1.25.0
27
27
  Requires-Dist: json_repair>=0.54.2
28
28
  Requires-Dist: tiktoken>=0.7.0
29
29
  Requires-Dist: google-genai>=0.3.0
30
+ Requires-Dist: charset-normalizer>=3.0.0
30
31
  Provides-Extra: dev
31
32
  Requires-Dist: pytest>=7.0.0; extra == "dev"
32
33
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -99,27 +100,22 @@ pip install git+https://github.com/quantmew/ripperdoc.git
99
100
  Or install from source:
100
101
  ```bash
101
102
  # Clone the repository
102
- git clone <repository-url>
103
- cd Ripperdoc
103
+ git clone https://github.com/quantmew/ripperdoc.git
104
+ cd ripperdoc
104
105
 
105
106
  # Install from source
106
107
  pip install -e .
107
108
  ```
108
109
 
109
- ### Configuration
110
110
 
111
- Set your API key as an environment variable:
112
- ```bash
113
- export OPENAI_API_KEY="your-api-key-here"
114
- # or for Anthropic Claude
115
- export ANTHROPIC_API_KEY="your-api-key-here"
116
- ```
117
111
 
118
112
  ## Usage
119
113
 
120
114
  ### Interactive Mode (Recommended)
121
115
  ```bash
122
116
  ripperdoc
117
+ # or use the short alias
118
+ rd
123
119
  ```
124
120
 
125
121
  This launches an interactive session where you can:
@@ -128,6 +124,13 @@ This launches an interactive session where you can:
128
124
  - Execute commands
129
125
  - Navigate and explore files
130
126
 
127
+ **Options:**
128
+ - `--yolo` - Skip permission prompts (safe mode is on by default)
129
+ - `--model <model_name>` - Specify a model (e.g., `claude-sonnet-4-20250514`, `gpt-4o`)
130
+ - `--tools <tool_list>` - Filter available tools (comma-separated, or "" for none)
131
+ - `--no-mcp` - Disable MCP server integration
132
+ - `--verbose` - Enable verbose logging
133
+
131
134
  ### Python SDK (headless)
132
135
 
133
136
  Use Ripperdoc without the terminal UI via the new Python SDK. See [docs/SDK_USAGE.md](docs/SDK_USAGE.md) for examples of the one-shot `query` helper and the session-based `RipperdocClient`. 中文指南见 [docs/SDK_USAGE_CN.md](docs/SDK_USAGE_CN.md)。
@@ -143,38 +146,67 @@ See the [examples/](examples/) directory for complete SDK usage examples.
143
146
 
144
147
  ### Safe Mode Permissions
145
148
 
146
- Safe mode is the default. Use `--yolo` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
149
+ Safe mode is enabled by default. When prompted:
150
+ - Type `y` or `yes` to allow a single operation
151
+ - Type `a` or `always` to allow all operations of that type for the session
152
+ - Type `n` or `no` to deny the operation
153
+
154
+ Use `--yolo` flag to skip all permission prompts:
155
+ ```bash
156
+ ripperdoc --yolo
157
+ ```
147
158
 
148
159
  ### Agent Skills
149
160
 
150
161
  Extend Ripperdoc with reusable Skill bundles:
151
162
 
152
- - Personal skills live in `~/.ripperdoc/skills/<skill-name>/SKILL.md`
153
- - Project skills live in `.ripperdoc/skills/<skill-name>/SKILL.md` and can be checked into git
154
- - Each `SKILL.md` starts with YAML frontmatter (`name`, `description`, optional `allowed-tools`, `model`, `max-thinking-tokens`, `disable-model-invocation`) followed by the instructions; add supporting files alongside it
155
- - Model and max-thinking-token hints from skills are applied automatically for the rest of the session after you load them with the `Skill` tool
156
- - Ripperdoc exposes skill names/descriptions in the system prompt and loads full content on demand via the `Skill` tool
163
+ - **Personal skills**: `~/.ripperdoc/skills/<skill-name>/SKILL.md`
164
+ - **Project skills**: `.ripperdoc/skills/<skill-name>/SKILL.md` (can be checked into git)
165
+ - Each `SKILL.md` starts with YAML frontmatter:
166
+ - `name` - Skill identifier
167
+ - `description` - What the skill does
168
+ - `allowed-tools` (optional) - Restrict which tools the skill can use
169
+ - `model` (optional) - Suggest a specific model for this skill
170
+ - `max-thinking-tokens` (optional) - Control thinking budget
171
+ - `disable-model-invocation` (optional) - Use skill without calling the model
172
+ - Add supporting files alongside `SKILL.md` as needed
173
+ - Skills are auto-discovered and loaded on demand via the `Skill` tool
174
+
175
+ **Built-in skills:** PDF manipulation (`pdf`), PowerPoint (`pptx`), Excel (`xlsx`)
157
176
 
158
177
  ## Examples
159
178
 
160
179
  ### Code Analysis
161
180
  ```
162
181
  > Can you explain what this function does?
182
+ > Find all references to the `parse_config` function
163
183
  ```
164
184
 
165
185
  ### File Operations
166
186
  ```
167
187
  > Read the main.py file and suggest improvements
188
+ > Create a new component called UserProfile.tsx
189
+ > Update all imports to use the new package structure
168
190
  ```
169
191
 
170
192
  ### Code Generation
171
193
  ```
172
194
  > Create a new Python script that implements a REST API client
195
+ > Generate unit tests for the auth module
196
+ > Add error handling to the database connection code
173
197
  ```
174
198
 
175
199
  ### Project Navigation
176
200
  ```
177
201
  > Show me all the Python files in the project
202
+ > Find where the user authentication logic is implemented
203
+ > List all API endpoints in the project
204
+ ```
205
+
206
+ ### MCP Integration
207
+ ```
208
+ > What MCP servers are available?
209
+ > Query the context7 documentation for React hooks
178
210
  ```
179
211
 
180
212
  ## Development
@@ -56,27 +56,22 @@ pip install git+https://github.com/quantmew/ripperdoc.git
56
56
  Or install from source:
57
57
  ```bash
58
58
  # Clone the repository
59
- git clone <repository-url>
60
- cd Ripperdoc
59
+ git clone https://github.com/quantmew/ripperdoc.git
60
+ cd ripperdoc
61
61
 
62
62
  # Install from source
63
63
  pip install -e .
64
64
  ```
65
65
 
66
- ### Configuration
67
66
 
68
- Set your API key as an environment variable:
69
- ```bash
70
- export OPENAI_API_KEY="your-api-key-here"
71
- # or for Anthropic Claude
72
- export ANTHROPIC_API_KEY="your-api-key-here"
73
- ```
74
67
 
75
68
  ## Usage
76
69
 
77
70
  ### Interactive Mode (Recommended)
78
71
  ```bash
79
72
  ripperdoc
73
+ # or use the short alias
74
+ rd
80
75
  ```
81
76
 
82
77
  This launches an interactive session where you can:
@@ -85,6 +80,13 @@ This launches an interactive session where you can:
85
80
  - Execute commands
86
81
  - Navigate and explore files
87
82
 
83
+ **Options:**
84
+ - `--yolo` - Skip permission prompts (safe mode is on by default)
85
+ - `--model <model_name>` - Specify a model (e.g., `claude-sonnet-4-20250514`, `gpt-4o`)
86
+ - `--tools <tool_list>` - Filter available tools (comma-separated, or "" for none)
87
+ - `--no-mcp` - Disable MCP server integration
88
+ - `--verbose` - Enable verbose logging
89
+
88
90
  ### Python SDK (headless)
89
91
 
90
92
  Use Ripperdoc without the terminal UI via the new Python SDK. See [docs/SDK_USAGE.md](docs/SDK_USAGE.md) for examples of the one-shot `query` helper and the session-based `RipperdocClient`. 中文指南见 [docs/SDK_USAGE_CN.md](docs/SDK_USAGE_CN.md)。
@@ -100,38 +102,67 @@ See the [examples/](examples/) directory for complete SDK usage examples.
100
102
 
101
103
  ### Safe Mode Permissions
102
104
 
103
- Safe mode is the default. Use `--yolo` to skip permission prompts. Choose `a`/`always` to allow a tool for the current session (not persisted across sessions).
105
+ Safe mode is enabled by default. When prompted:
106
+ - Type `y` or `yes` to allow a single operation
107
+ - Type `a` or `always` to allow all operations of that type for the session
108
+ - Type `n` or `no` to deny the operation
109
+
110
+ Use `--yolo` flag to skip all permission prompts:
111
+ ```bash
112
+ ripperdoc --yolo
113
+ ```
104
114
 
105
115
  ### Agent Skills
106
116
 
107
117
  Extend Ripperdoc with reusable Skill bundles:
108
118
 
109
- - Personal skills live in `~/.ripperdoc/skills/<skill-name>/SKILL.md`
110
- - Project skills live in `.ripperdoc/skills/<skill-name>/SKILL.md` and can be checked into git
111
- - Each `SKILL.md` starts with YAML frontmatter (`name`, `description`, optional `allowed-tools`, `model`, `max-thinking-tokens`, `disable-model-invocation`) followed by the instructions; add supporting files alongside it
112
- - Model and max-thinking-token hints from skills are applied automatically for the rest of the session after you load them with the `Skill` tool
113
- - Ripperdoc exposes skill names/descriptions in the system prompt and loads full content on demand via the `Skill` tool
119
+ - **Personal skills**: `~/.ripperdoc/skills/<skill-name>/SKILL.md`
120
+ - **Project skills**: `.ripperdoc/skills/<skill-name>/SKILL.md` (can be checked into git)
121
+ - Each `SKILL.md` starts with YAML frontmatter:
122
+ - `name` - Skill identifier
123
+ - `description` - What the skill does
124
+ - `allowed-tools` (optional) - Restrict which tools the skill can use
125
+ - `model` (optional) - Suggest a specific model for this skill
126
+ - `max-thinking-tokens` (optional) - Control thinking budget
127
+ - `disable-model-invocation` (optional) - Use skill without calling the model
128
+ - Add supporting files alongside `SKILL.md` as needed
129
+ - Skills are auto-discovered and loaded on demand via the `Skill` tool
130
+
131
+ **Built-in skills:** PDF manipulation (`pdf`), PowerPoint (`pptx`), Excel (`xlsx`)
114
132
 
115
133
  ## Examples
116
134
 
117
135
  ### Code Analysis
118
136
  ```
119
137
  > Can you explain what this function does?
138
+ > Find all references to the `parse_config` function
120
139
  ```
121
140
 
122
141
  ### File Operations
123
142
  ```
124
143
  > Read the main.py file and suggest improvements
144
+ > Create a new component called UserProfile.tsx
145
+ > Update all imports to use the new package structure
125
146
  ```
126
147
 
127
148
  ### Code Generation
128
149
  ```
129
150
  > Create a new Python script that implements a REST API client
151
+ > Generate unit tests for the auth module
152
+ > Add error handling to the database connection code
130
153
  ```
131
154
 
132
155
  ### Project Navigation
133
156
  ```
134
157
  > Show me all the Python files in the project
158
+ > Find where the user authentication logic is implemented
159
+ > List all API endpoints in the project
160
+ ```
161
+
162
+ ### MCP Integration
163
+ ```
164
+ > What MCP servers are available?
165
+ > Query the context7 documentation for React hooks
135
166
  ```
136
167
 
137
168
  ## Development
@@ -32,10 +32,11 @@ dependencies = [
32
32
  "aiofiles>=23.0.0",
33
33
  "prompt-toolkit>=3.0.0",
34
34
  "PyYAML>=6.0.0",
35
- "mcp[cli]>=1.22.0",
35
+ "mcp[cli]>=1.25.0", # Fixed stdio_client async generator cleanup issue
36
36
  "json_repair>=0.54.2",
37
37
  "tiktoken>=0.7.0",
38
38
  "google-genai>=0.3.0",
39
+ "charset-normalizer>=3.0.0",
39
40
  ]
40
41
 
41
42
  [project.optional-dependencies]
@@ -1,3 +1,3 @@
1
1
  """Ripperdoc - AI-powered coding agent."""
2
2
 
3
- __version__ = "0.2.10"
3
+ __version__ = "0.3.0"
@@ -44,7 +44,6 @@ from ripperdoc.utils.log import enable_session_file_logging, get_logger
44
44
 
45
45
  from rich.console import Console
46
46
  from rich.markdown import Markdown
47
- from rich.panel import Panel
48
47
  from rich.markup import escape
49
48
 
50
49
  console = Console()
@@ -98,7 +97,6 @@ async def run_query(
98
97
  model: Optional[str] = None,
99
98
  ) -> None:
100
99
  """Run a single query and print the response."""
101
-
102
100
  logger.info(
103
101
  "[cli] Running single prompt session",
104
102
  extra={
@@ -212,55 +210,37 @@ async def run_query(
212
210
  mcp_instructions=mcp_instructions,
213
211
  )
214
212
 
215
- # Run the query
213
+ # Run the query - collect final response text
214
+ final_response_parts: List[str] = []
216
215
  try:
217
216
  async for message in query(
218
217
  messages, system_prompt, context, query_context, can_use_tool
219
218
  ):
220
219
  if message.type == "assistant" and hasattr(message, "message"):
221
- # Print assistant message
220
+ # Collect assistant message text for final output
222
221
  if isinstance(message.message.content, str):
223
- console.print(
224
- Panel(
225
- Markdown(message.message.content),
226
- title="Ripperdoc",
227
- border_style="cyan",
228
- padding=(0, 1),
229
- )
230
- )
222
+ final_response_parts.append(message.message.content)
231
223
  else:
232
224
  # Handle structured content
233
225
  for block in message.message.content:
234
226
  if isinstance(block, dict):
235
227
  if block.get("type") == "text":
236
- console.print(
237
- Panel(
238
- Markdown(block["text"]),
239
- title="Ripperdoc",
240
- border_style="cyan",
241
- padding=(0, 1),
242
- )
243
- )
228
+ final_response_parts.append(block["text"])
244
229
  else:
245
230
  if hasattr(block, "type") and block.type == "text":
246
- console.print(
247
- Panel(
248
- Markdown(block.text or ""),
249
- title="Ripperdoc",
250
- border_style="cyan",
251
- padding=(0, 1),
252
- )
253
- )
254
-
255
- elif message.type == "progress" and hasattr(message, "content"):
256
- # Print progress
257
- if verbose:
258
- console.print(f"[dim]Progress: {escape(str(message.content))}[/dim]")
231
+ final_response_parts.append(block.text or "")
232
+
233
+ # Skip progress messages entirely for -p mode
259
234
 
260
235
  # Add message to history
261
236
  messages.append(message) # type: ignore[arg-type]
262
237
  session_history.append(message) # type: ignore[arg-type]
263
238
 
239
+ # Print final response as clean markdown (no panel, no decoration)
240
+ if final_response_parts:
241
+ final_text = "\n".join(final_response_parts)
242
+ console.print(Markdown(final_text))
243
+
264
244
  except KeyboardInterrupt:
265
245
  console.print("\n[yellow]Interrupted by user[/yellow]")
266
246
  except asyncio.CancelledError:
@@ -308,7 +288,6 @@ async def run_query(
308
288
  logger.debug("[cli] Shutdown MCP runtime", extra={"session_id": session_id})
309
289
 
310
290
 
311
-
312
291
  @click.group(invoke_without_command=True)
313
292
  @click.version_option(version=__version__)
314
293
  @click.option("--cwd", type=click.Path(exists=True), help="Working directory")
@@ -374,19 +353,51 @@ def cli(
374
353
  ) -> None:
375
354
  """Ripperdoc - AI-powered coding agent"""
376
355
  session_id = str(uuid.uuid4())
356
+ cwd_changed: Optional[str] = None
377
357
 
378
358
  # Set working directory
379
359
  if cwd:
380
360
  import os
381
361
 
382
362
  os.chdir(cwd)
363
+ cwd_changed = cwd
364
+
365
+ project_path = Path.cwd()
366
+
367
+ # Handle --continue option: load the most recent session
368
+ resume_messages = None
369
+ most_recent = None
370
+ if continue_session:
371
+ summaries = list_session_summaries(project_path)
372
+ if summaries:
373
+ most_recent = summaries[0]
374
+ session_id = most_recent.session_id
375
+ resume_messages = load_session_messages(project_path, session_id)
376
+ console.print(f"[dim]Continuing session: {most_recent.last_prompt}[/dim]")
377
+ else:
378
+ console.print("[yellow]No previous sessions found in this directory.[/yellow]")
379
+
380
+ log_file = enable_session_file_logging(project_path, session_id)
381
+
382
+ if cwd_changed:
383
383
  logger.debug(
384
384
  "[cli] Changed working directory via --cwd",
385
- extra={"cwd": cwd, "session_id": session_id},
385
+ extra={"cwd": cwd_changed, "session_id": session_id},
386
386
  )
387
387
 
388
- project_path = Path.cwd()
389
- log_file = enable_session_file_logging(project_path, session_id)
388
+ if most_recent:
389
+ logger.info(
390
+ "[cli] Continuing session",
391
+ extra={
392
+ "session_id": session_id,
393
+ "message_count": len(resume_messages) if resume_messages else 0,
394
+ "last_prompt": most_recent.last_prompt,
395
+ "log_file": str(log_file),
396
+ },
397
+ )
398
+ elif continue_session:
399
+ logger.warning("[cli] No previous sessions found to continue")
400
+
390
401
  logger.info(
391
402
  "[cli] Starting CLI invocation",
392
403
  extra={
@@ -412,26 +423,39 @@ def cli(
412
423
  # Parse --tools option
413
424
  allowed_tools = parse_tools_option(tools)
414
425
 
415
- # Handle --continue option: load the most recent session
416
- resume_messages = None
417
- if continue_session:
418
- summaries = list_session_summaries(project_path)
419
- if summaries:
420
- most_recent = summaries[0]
421
- session_id = most_recent.session_id
422
- resume_messages = load_session_messages(project_path, session_id)
423
- logger.info(
424
- "[cli] Continuing session",
425
- extra={
426
- "session_id": session_id,
427
- "message_count": len(resume_messages),
428
- "last_prompt": most_recent.last_prompt,
429
- },
430
- )
431
- console.print(f"[dim]Continuing session: {most_recent.last_prompt}[/dim]")
432
- else:
433
- logger.warning("[cli] No previous sessions found to continue")
434
- console.print("[yellow]No previous sessions found in this directory.[/yellow]")
426
+ # Handle piped stdin input
427
+ # - With -p flag: Not applicable (prompt comes from -p argument)
428
+ # - Without -p: stdin becomes the initial query in interactive mode
429
+ initial_query: Optional[str] = None
430
+ if prompt is None and ctx.invoked_subcommand is None:
431
+ stdin_stream = click.get_text_stream("stdin")
432
+ try:
433
+ stdin_is_tty = stdin_stream.isatty()
434
+ except Exception:
435
+ stdin_is_tty = True
436
+
437
+ if not stdin_is_tty:
438
+ try:
439
+ stdin_data = stdin_stream.read()
440
+ except (OSError, ValueError) as exc:
441
+ logger.warning(
442
+ "[cli] Failed to read stdin for initial query: %s: %s",
443
+ type(exc).__name__,
444
+ exc,
445
+ extra={"session_id": session_id},
446
+ )
447
+ else:
448
+ trimmed = stdin_data.rstrip("\n")
449
+ if trimmed.strip():
450
+ initial_query = trimmed
451
+ logger.info(
452
+ "[cli] Received initial query from stdin",
453
+ extra={
454
+ "session_id": session_id,
455
+ "query_length": len(initial_query),
456
+ "query_preview": initial_query[:200],
457
+ },
458
+ )
435
459
 
436
460
  logger.debug(
437
461
  "[cli] Configuration initialized",
@@ -480,6 +504,7 @@ def cli(
480
504
  append_system_prompt=append_system_prompt,
481
505
  model=model,
482
506
  resume_messages=resume_messages,
507
+ initial_query=initial_query,
483
508
  )
484
509
  return
485
510
 
@@ -507,6 +532,88 @@ def config_cmd() -> None:
507
532
  console.print()
508
533
 
509
534
 
535
+ @cli.command(name="stdio")
536
+ @click.option(
537
+ "--input-format",
538
+ type=click.Choice(["stream-json", "auto"]),
539
+ default="stream-json",
540
+ help="Input format for messages.",
541
+ )
542
+ @click.option(
543
+ "--output-format",
544
+ type=click.Choice(["stream-json"]),
545
+ default="stream-json",
546
+ help="Output format for messages.",
547
+ )
548
+ @click.option(
549
+ "--model",
550
+ type=str,
551
+ default=None,
552
+ help="Model profile for the current session.",
553
+ )
554
+ @click.option(
555
+ "--permission-mode",
556
+ type=click.Choice(["default", "acceptEdits", "plan", "bypassPermissions"]),
557
+ default="default",
558
+ help="Permission mode for tool usage.",
559
+ )
560
+ @click.option(
561
+ "--max-turns",
562
+ type=int,
563
+ default=None,
564
+ help="Maximum number of conversation turns.",
565
+ )
566
+ @click.option(
567
+ "--system-prompt",
568
+ type=str,
569
+ default=None,
570
+ help="System prompt to use for the session.",
571
+ )
572
+ @click.option(
573
+ "--print",
574
+ "-p",
575
+ is_flag=True,
576
+ help="Print mode (for single prompt queries).",
577
+ )
578
+ @click.option(
579
+ "--",
580
+ "prompt",
581
+ type=str,
582
+ default=None,
583
+ help="Direct prompt (for print mode).",
584
+ )
585
+ def stdio_cmd(
586
+ input_format: str,
587
+ output_format: str,
588
+ model: str | None,
589
+ permission_mode: str,
590
+ max_turns: int | None,
591
+ system_prompt: str | None,
592
+ print: bool,
593
+ prompt: str | None,
594
+ ) -> None:
595
+ """Stdio mode for SDK subprocess communication.
596
+
597
+ This command enables Ripperdoc to communicate with SDKs via JSON Control
598
+ Protocol over stdin/stdout.
599
+ """
600
+ from ripperdoc.protocol.stdio import _run_stdio
601
+ import asyncio
602
+
603
+ asyncio.run(
604
+ _run_stdio(
605
+ input_format=input_format,
606
+ output_format=output_format,
607
+ model=model,
608
+ permission_mode=permission_mode,
609
+ max_turns=max_turns,
610
+ system_prompt=system_prompt,
611
+ print_mode=print,
612
+ prompt=prompt,
613
+ )
614
+ )
615
+
616
+
510
617
  @cli.command(name="version")
511
618
  def version_cmd() -> None:
512
619
  """Show version information"""
@@ -21,9 +21,11 @@ from .mcp_cmd import command as mcp_command
21
21
  from .models_cmd import command as models_command
22
22
  from .permissions_cmd import command as permissions_command
23
23
  from .resume_cmd import command as resume_command
24
+ from .skills_cmd import command as skills_command
24
25
  from .stats_cmd import command as stats_command
25
26
  from .tasks_cmd import command as tasks_command
26
27
  from .status_cmd import command as status_command
28
+ from .themes_cmd import command as themes_command
27
29
  from .todos_cmd import command as todos_command
28
30
  from .tools_cmd import command as tools_command
29
31
 
@@ -64,7 +66,9 @@ ALL_COMMANDS: List[SlashCommand] = [
64
66
  context_command,
65
67
  compact_command,
66
68
  resume_command,
69
+ skills_command,
67
70
  agents_command,
71
+ themes_command,
68
72
  ]
69
73
 
70
74
  COMMAND_REGISTRY: Dict[str, SlashCommand] = _build_registry(ALL_COMMANDS)
@@ -18,7 +18,7 @@ from ripperdoc.tools.task_tool import (
18
18
  )
19
19
  from ripperdoc.utils.log import get_logger
20
20
 
21
- from typing import Any, Dict, Optional
21
+ from typing import Any, Dict
22
22
  from .base import SlashCommand
23
23
 
24
24
  logger = get_logger()
@@ -101,9 +101,7 @@ def _handle(ui: Any, trimmed_arg: str) -> bool:
101
101
  for run_id in sorted(run_ids):
102
102
  snapshot: Dict[Any, Any] = get_agent_run_snapshot(run_id) or {}
103
103
  result_text = snapshot.get("result_text") or snapshot.get("error") or ""
104
- result_preview = (
105
- result_text if len(result_text) <= 80 else result_text[:77] + "..."
106
- )
104
+ result_preview = result_text if len(result_text) <= 80 else result_text[:77] + "..."
107
105
  table.add_row(
108
106
  escape(run_id),
109
107
  escape(snapshot.get("status") or "unknown"),
@@ -136,9 +134,7 @@ def _handle(ui: Any, trimmed_arg: str) -> bool:
136
134
  details.add_row("Status", escape(snapshot.get("status") or "unknown"))
137
135
  details.add_row("Agent", escape(snapshot.get("agent_type") or "unknown"))
138
136
  details.add_row("Duration", _format_duration(snapshot.get("duration_ms")))
139
- details.add_row(
140
- "Background", "yes" if snapshot.get("is_background") else "no"
141
- )
137
+ details.add_row("Background", "yes" if snapshot.get("is_background") else "no")
142
138
  if snapshot.get("model_used"):
143
139
  details.add_row("Model", escape(str(snapshot.get("model_used"))))
144
140
  if snapshot.get("tool_use_count"):