ripperdoc 0.2.6__py3-none-any.whl → 0.2.8__py3-none-any.whl

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 (44) hide show
  1. ripperdoc/__init__.py +1 -1
  2. ripperdoc/cli/cli.py +5 -0
  3. ripperdoc/cli/commands/__init__.py +71 -6
  4. ripperdoc/cli/commands/clear_cmd.py +1 -0
  5. ripperdoc/cli/commands/exit_cmd.py +1 -1
  6. ripperdoc/cli/commands/help_cmd.py +11 -1
  7. ripperdoc/cli/commands/hooks_cmd.py +636 -0
  8. ripperdoc/cli/commands/permissions_cmd.py +36 -34
  9. ripperdoc/cli/commands/resume_cmd.py +71 -37
  10. ripperdoc/cli/ui/file_mention_completer.py +276 -0
  11. ripperdoc/cli/ui/helpers.py +100 -3
  12. ripperdoc/cli/ui/interrupt_handler.py +175 -0
  13. ripperdoc/cli/ui/message_display.py +249 -0
  14. ripperdoc/cli/ui/panels.py +63 -0
  15. ripperdoc/cli/ui/rich_ui.py +233 -648
  16. ripperdoc/cli/ui/tool_renderers.py +2 -2
  17. ripperdoc/core/agents.py +4 -4
  18. ripperdoc/core/custom_commands.py +411 -0
  19. ripperdoc/core/hooks/__init__.py +99 -0
  20. ripperdoc/core/hooks/config.py +303 -0
  21. ripperdoc/core/hooks/events.py +540 -0
  22. ripperdoc/core/hooks/executor.py +498 -0
  23. ripperdoc/core/hooks/integration.py +353 -0
  24. ripperdoc/core/hooks/manager.py +720 -0
  25. ripperdoc/core/providers/anthropic.py +476 -69
  26. ripperdoc/core/query.py +61 -4
  27. ripperdoc/core/query_utils.py +1 -1
  28. ripperdoc/core/tool.py +1 -1
  29. ripperdoc/tools/bash_tool.py +5 -5
  30. ripperdoc/tools/file_edit_tool.py +2 -2
  31. ripperdoc/tools/file_read_tool.py +2 -2
  32. ripperdoc/tools/multi_edit_tool.py +1 -1
  33. ripperdoc/utils/conversation_compaction.py +476 -0
  34. ripperdoc/utils/message_compaction.py +109 -154
  35. ripperdoc/utils/message_formatting.py +216 -0
  36. ripperdoc/utils/messages.py +31 -9
  37. ripperdoc/utils/path_ignore.py +3 -4
  38. ripperdoc/utils/session_history.py +19 -7
  39. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/METADATA +24 -3
  40. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/RECORD +44 -30
  41. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/WHEEL +0 -0
  42. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/entry_points.txt +0 -0
  43. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/licenses/LICENSE +0 -0
  44. {ripperdoc-0.2.6.dist-info → ripperdoc-0.2.8.dist-info}/top_level.txt +0 -0
ripperdoc/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Ripperdoc - AI-powered coding agent."""
2
2
 
3
- __version__ = "0.2.6"
3
+ __version__ = "0.2.8"
ripperdoc/cli/cli.py CHANGED
@@ -22,6 +22,7 @@ from ripperdoc.core.default_tools import get_default_tools
22
22
  from ripperdoc.core.query import query, QueryContext
23
23
  from ripperdoc.core.system_prompt import build_system_prompt
24
24
  from ripperdoc.core.skills import build_skill_summary, load_all_skills
25
+ from ripperdoc.core.hooks.manager import hook_manager
25
26
  from ripperdoc.utils.messages import create_user_message
26
27
  from ripperdoc.utils.memory import build_memory_instructions
27
28
  from ripperdoc.core.permissions import make_permission_checker
@@ -70,6 +71,10 @@ async def run_query(
70
71
  project_path = Path.cwd()
71
72
  can_use_tool = make_permission_checker(project_path, safe_mode) if safe_mode else None
72
73
 
74
+ # Initialize hook manager
75
+ hook_manager.set_project_dir(project_path)
76
+ hook_manager.set_session_id(session_id)
77
+
73
78
  # Create initial user message
74
79
  from ripperdoc.utils.messages import UserMessage, AssistantMessage, ProgressMessage
75
80
 
@@ -1,8 +1,9 @@
1
- """Slash command registry."""
1
+ """Slash command registry with custom command support."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Dict, List
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional, Tuple, Union
6
7
 
7
8
  from .base import SlashCommand
8
9
  from .agents_cmd import command as agents_command
@@ -14,6 +15,7 @@ from .context_cmd import command as context_command
14
15
  from .doctor_cmd import command as doctor_command
15
16
  from .exit_cmd import command as exit_command
16
17
  from .help_cmd import command as help_command
18
+ from .hooks_cmd import command as hooks_command
17
19
  from .memory_cmd import command as memory_command
18
20
  from .mcp_cmd import command as mcp_command
19
21
  from .models_cmd import command as models_command
@@ -24,6 +26,13 @@ from .status_cmd import command as status_command
24
26
  from .todos_cmd import command as todos_command
25
27
  from .tools_cmd import command as tools_command
26
28
 
29
+ from ripperdoc.core.custom_commands import (
30
+ CustomCommandDefinition,
31
+ load_all_custom_commands,
32
+ find_custom_command,
33
+ expand_command_content,
34
+ )
35
+
27
36
 
28
37
  def _build_registry(commands: List[SlashCommand]) -> Dict[str, SlashCommand]:
29
38
  """Map command names and aliases to SlashCommand entries."""
@@ -49,6 +58,7 @@ ALL_COMMANDS: List[SlashCommand] = [
49
58
  tasks_command,
50
59
  todos_command,
51
60
  mcp_command,
61
+ hooks_command,
52
62
  cost_command,
53
63
  context_command,
54
64
  compact_command,
@@ -58,25 +68,80 @@ ALL_COMMANDS: List[SlashCommand] = [
58
68
 
59
69
  COMMAND_REGISTRY: Dict[str, SlashCommand] = _build_registry(ALL_COMMANDS)
60
70
 
71
+ # Cache for custom commands
72
+ _custom_commands_cache: Optional[Tuple[Path, List[CustomCommandDefinition]]] = None
73
+
74
+
75
+ def _get_custom_commands(project_path: Optional[Path] = None) -> List[CustomCommandDefinition]:
76
+ """Get custom commands with caching."""
77
+ global _custom_commands_cache
78
+ current_path = (project_path or Path.cwd()).resolve()
79
+
80
+ # Return cached commands if same project
81
+ if _custom_commands_cache and _custom_commands_cache[0] == current_path:
82
+ return _custom_commands_cache[1]
83
+
84
+ # Load and cache
85
+ result = load_all_custom_commands(project_path=current_path)
86
+ _custom_commands_cache = (current_path, result.commands)
87
+ return result.commands
88
+
89
+
90
+ def refresh_custom_commands(project_path: Optional[Path] = None) -> List[CustomCommandDefinition]:
91
+ """Force reload custom commands."""
92
+ global _custom_commands_cache
93
+ _custom_commands_cache = None
94
+ return _get_custom_commands(project_path)
95
+
61
96
 
62
97
  def list_slash_commands() -> List[SlashCommand]:
63
98
  """Return the ordered list of base slash commands (no aliases)."""
64
99
  return ALL_COMMANDS
65
100
 
66
101
 
102
+ def list_custom_commands(project_path: Optional[Path] = None) -> List[CustomCommandDefinition]:
103
+ """Return all loaded custom commands."""
104
+ return _get_custom_commands(project_path)
105
+
106
+
67
107
  def get_slash_command(name: str) -> SlashCommand | None:
68
- """Return a command by name or alias."""
108
+ """Return a built-in command by name or alias."""
69
109
  return COMMAND_REGISTRY.get(name)
70
110
 
71
111
 
72
- def slash_command_completions() -> List[tuple[str, SlashCommand]]:
73
- """Return (name, command) pairs for completion including aliases."""
74
- return list(COMMAND_REGISTRY.items())
112
+ def get_custom_command(
113
+ name: str, project_path: Optional[Path] = None
114
+ ) -> CustomCommandDefinition | None:
115
+ """Return a custom command by name."""
116
+ commands = _get_custom_commands(project_path)
117
+ return next((cmd for cmd in commands if cmd.name == name), None)
118
+
119
+
120
+ def slash_command_completions(
121
+ project_path: Optional[Path] = None,
122
+ ) -> List[Tuple[str, Union[SlashCommand, CustomCommandDefinition]]]:
123
+ """Return (name, command) pairs for completion including aliases and custom commands."""
124
+ completions: List[Tuple[str, Union[SlashCommand, CustomCommandDefinition]]] = []
125
+
126
+ # Add built-in commands
127
+ completions.extend(list(COMMAND_REGISTRY.items()))
128
+
129
+ # Add custom commands
130
+ custom_cmds = _get_custom_commands(project_path)
131
+ for cmd in custom_cmds:
132
+ completions.append((cmd.name, cmd))
133
+
134
+ return completions
75
135
 
76
136
 
77
137
  __all__ = [
78
138
  "SlashCommand",
139
+ "CustomCommandDefinition",
79
140
  "list_slash_commands",
141
+ "list_custom_commands",
80
142
  "get_slash_command",
143
+ "get_custom_command",
81
144
  "slash_command_completions",
145
+ "refresh_custom_commands",
146
+ "expand_command_content",
82
147
  ]
@@ -12,6 +12,7 @@ command = SlashCommand(
12
12
  name="clear",
13
13
  description="Clear conversation history",
14
14
  handler=_handle,
15
+ aliases=("new",)
15
16
  )
16
17
 
17
18
 
@@ -12,7 +12,7 @@ command = SlashCommand(
12
12
  name="exit",
13
13
  description="Exit Ripperdoc",
14
14
  handler=_handle,
15
- aliases=(),
15
+ aliases=("quit",),
16
16
  )
17
17
 
18
18
 
@@ -6,7 +6,17 @@ def _handle(ui: Any, _: str) -> bool:
6
6
  ui.console.print("\n[bold]Available Slash Commands:[/bold]")
7
7
  for cmd in ui.command_list:
8
8
  alias_text = f" (aliases: {', '.join(cmd.aliases)})" if cmd.aliases else ""
9
- ui.console.print(f" /{cmd.name:<8} - {cmd.description}{alias_text}")
9
+ ui.console.print(f" /{cmd.name:<12} - {cmd.description}{alias_text}")
10
+
11
+ # Show custom commands if any
12
+ custom_cmds = getattr(ui, "_custom_command_list", [])
13
+ if custom_cmds:
14
+ ui.console.print("\n[bold]Custom Commands:[/bold]")
15
+ for cmd in sorted(custom_cmds, key=lambda c: c.name):
16
+ hint = f" {cmd.argument_hint}" if cmd.argument_hint else ""
17
+ location = f" ({cmd.location.value})" if cmd.location else ""
18
+ ui.console.print(f" /{cmd.name:<12}{hint} - {cmd.description}{location}")
19
+
10
20
  return True
11
21
 
12
22