groknroll 2.0.0__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 (62) hide show
  1. groknroll/__init__.py +36 -0
  2. groknroll/__main__.py +9 -0
  3. groknroll/agents/__init__.py +18 -0
  4. groknroll/agents/agent_manager.py +187 -0
  5. groknroll/agents/base_agent.py +118 -0
  6. groknroll/agents/build_agent.py +231 -0
  7. groknroll/agents/plan_agent.py +215 -0
  8. groknroll/cli/__init__.py +7 -0
  9. groknroll/cli/enhanced_cli.py +372 -0
  10. groknroll/cli/large_codebase_cli.py +413 -0
  11. groknroll/cli/main.py +331 -0
  12. groknroll/cli/rlm_commands.py +258 -0
  13. groknroll/clients/__init__.py +63 -0
  14. groknroll/clients/anthropic.py +112 -0
  15. groknroll/clients/azure_openai.py +142 -0
  16. groknroll/clients/base_lm.py +33 -0
  17. groknroll/clients/gemini.py +162 -0
  18. groknroll/clients/litellm.py +105 -0
  19. groknroll/clients/openai.py +129 -0
  20. groknroll/clients/portkey.py +94 -0
  21. groknroll/core/__init__.py +9 -0
  22. groknroll/core/agent.py +339 -0
  23. groknroll/core/comms_utils.py +264 -0
  24. groknroll/core/context.py +251 -0
  25. groknroll/core/exceptions.py +181 -0
  26. groknroll/core/large_codebase.py +564 -0
  27. groknroll/core/lm_handler.py +206 -0
  28. groknroll/core/rlm.py +446 -0
  29. groknroll/core/rlm_codebase.py +448 -0
  30. groknroll/core/rlm_integration.py +256 -0
  31. groknroll/core/types.py +276 -0
  32. groknroll/environments/__init__.py +34 -0
  33. groknroll/environments/base_env.py +182 -0
  34. groknroll/environments/constants.py +32 -0
  35. groknroll/environments/docker_repl.py +336 -0
  36. groknroll/environments/local_repl.py +388 -0
  37. groknroll/environments/modal_repl.py +502 -0
  38. groknroll/environments/prime_repl.py +588 -0
  39. groknroll/logger/__init__.py +4 -0
  40. groknroll/logger/rlm_logger.py +63 -0
  41. groknroll/logger/verbose.py +393 -0
  42. groknroll/operations/__init__.py +15 -0
  43. groknroll/operations/bash_ops.py +447 -0
  44. groknroll/operations/file_ops.py +473 -0
  45. groknroll/operations/git_ops.py +620 -0
  46. groknroll/oracle/__init__.py +11 -0
  47. groknroll/oracle/codebase_indexer.py +238 -0
  48. groknroll/oracle/oracle_agent.py +278 -0
  49. groknroll/setup.py +34 -0
  50. groknroll/storage/__init__.py +14 -0
  51. groknroll/storage/database.py +272 -0
  52. groknroll/storage/models.py +128 -0
  53. groknroll/utils/__init__.py +0 -0
  54. groknroll/utils/parsing.py +168 -0
  55. groknroll/utils/prompts.py +146 -0
  56. groknroll/utils/rlm_utils.py +19 -0
  57. groknroll-2.0.0.dist-info/METADATA +246 -0
  58. groknroll-2.0.0.dist-info/RECORD +62 -0
  59. groknroll-2.0.0.dist-info/WHEEL +5 -0
  60. groknroll-2.0.0.dist-info/entry_points.txt +3 -0
  61. groknroll-2.0.0.dist-info/licenses/LICENSE +21 -0
  62. groknroll-2.0.0.dist-info/top_level.txt +1 -0
groknroll/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ groknroll - The Ultimate CLI Coding Agent with RLM Core
3
+
4
+ Better than Claude Code. Local, unlimited context, autonomous.
5
+ Includes Recursive Language Models (RLM) for infinite context handling.
6
+ """
7
+
8
+ __version__ = "2.0.0"
9
+ __author__ = "Michael Thornton"
10
+
11
+ # RLM Core exports
12
+ from groknroll.core.rlm import RLM
13
+ from groknroll.core.types import RLMChatCompletion, REPLResult, RLMIteration
14
+ from groknroll.core.exceptions import RLMError, REPLExecutionError, CostLimitExceededError
15
+
16
+ # groknroll CLI exports
17
+ from groknroll.core.agent import GroknrollAgent
18
+
19
+ # Oracle Agent exports
20
+ from groknroll.oracle import OracleAgent, CodebaseIndexer
21
+
22
+ __all__ = [
23
+ # RLM Core
24
+ "RLM",
25
+ "RLMChatCompletion",
26
+ "REPLResult",
27
+ "RLMIteration",
28
+ "RLMError",
29
+ "REPLExecutionError",
30
+ "CostLimitExceededError",
31
+ # groknroll Agent
32
+ "GroknrollAgent",
33
+ # Oracle Agent
34
+ "OracleAgent",
35
+ "CodebaseIndexer",
36
+ ]
groknroll/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ """
2
+ groknroll CLI entry point
3
+ """
4
+
5
+ import sys
6
+ from groknroll.cli.main import main
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(main())
@@ -0,0 +1,18 @@
1
+ """
2
+ Multi-agent system for groknroll
3
+
4
+ Provides build and plan agents with different permission levels.
5
+ """
6
+
7
+ from groknroll.agents.base_agent import BaseAgent, AgentCapability
8
+ from groknroll.agents.build_agent import BuildAgent
9
+ from groknroll.agents.plan_agent import PlanAgent
10
+ from groknroll.agents.agent_manager import AgentManager
11
+
12
+ __all__ = [
13
+ "BaseAgent",
14
+ "AgentCapability",
15
+ "BuildAgent",
16
+ "PlanAgent",
17
+ "AgentManager",
18
+ ]
@@ -0,0 +1,187 @@
1
+ """
2
+ Agent Manager
3
+
4
+ Manages multiple agents and handles agent switching.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Dict, Optional, Any
9
+ from dataclasses import dataclass
10
+
11
+ from groknroll.agents.base_agent import BaseAgent, AgentResponse
12
+ from groknroll.agents.build_agent import BuildAgent
13
+ from groknroll.agents.plan_agent import PlanAgent
14
+
15
+
16
+ @dataclass
17
+ class AgentInfo:
18
+ """Information about an agent"""
19
+ name: str
20
+ description: str
21
+ capabilities: list[str]
22
+ is_active: bool
23
+
24
+
25
+ class AgentManager:
26
+ """
27
+ Agent Manager
28
+
29
+ Manages multiple agents and provides agent switching functionality.
30
+ Similar to OpenCode's Tab key agent switching.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ project_path: Path,
36
+ model: str = "gpt-4o-mini",
37
+ max_cost: float = 5.0,
38
+ timeout: int = 300
39
+ ):
40
+ """
41
+ Initialize agent manager
42
+
43
+ Args:
44
+ project_path: Project root path
45
+ model: Default model for agents
46
+ max_cost: Default max cost
47
+ timeout: Default timeout
48
+ """
49
+ self.project_path = project_path
50
+ self.model = model
51
+ self.max_cost = max_cost
52
+ self.timeout = timeout
53
+
54
+ # Initialize agents
55
+ self.agents: Dict[str, BaseAgent] = {
56
+ "build": BuildAgent(
57
+ project_path=project_path,
58
+ model=model,
59
+ max_cost=max_cost,
60
+ timeout=timeout
61
+ ),
62
+ "plan": PlanAgent(
63
+ project_path=project_path,
64
+ model=model,
65
+ max_cost=max_cost,
66
+ timeout=timeout
67
+ )
68
+ }
69
+
70
+ # Current agent
71
+ self.current_agent_name = "build" # Default to build agent
72
+
73
+ @property
74
+ def current_agent(self) -> BaseAgent:
75
+ """Get current active agent"""
76
+ return self.agents[self.current_agent_name]
77
+
78
+ def switch(self, agent_name: str) -> bool:
79
+ """
80
+ Switch to different agent
81
+
82
+ Args:
83
+ agent_name: Agent name (build/plan)
84
+
85
+ Returns:
86
+ True if switched successfully
87
+ """
88
+ if agent_name not in self.agents:
89
+ return False
90
+
91
+ self.current_agent_name = agent_name
92
+ return True
93
+
94
+ def execute(
95
+ self,
96
+ task: str,
97
+ agent: Optional[str] = None,
98
+ context: Optional[Dict[str, Any]] = None
99
+ ) -> AgentResponse:
100
+ """
101
+ Execute task with current or specified agent
102
+
103
+ Args:
104
+ task: Task description
105
+ agent: Agent name (uses current if None)
106
+ context: Additional context
107
+
108
+ Returns:
109
+ AgentResponse
110
+ """
111
+ # Use specified agent or current
112
+ agent_name = agent or self.current_agent_name
113
+
114
+ if agent_name not in self.agents:
115
+ return AgentResponse(
116
+ success=False,
117
+ message=f"Unknown agent: {agent_name}",
118
+ agent_name=agent_name,
119
+ task=task
120
+ )
121
+
122
+ # Execute with agent
123
+ return self.agents[agent_name].execute(task, context)
124
+
125
+ def get_agent(self, name: str) -> Optional[BaseAgent]:
126
+ """Get agent by name"""
127
+ return self.agents.get(name)
128
+
129
+ def list_agents(self) -> list[AgentInfo]:
130
+ """List all available agents"""
131
+ return [
132
+ AgentInfo(
133
+ name=name,
134
+ description=agent.config.description,
135
+ capabilities=agent.get_capabilities(),
136
+ is_active=(name == self.current_agent_name)
137
+ )
138
+ for name, agent in self.agents.items()
139
+ ]
140
+
141
+ def get_stats(self) -> Dict[str, Any]:
142
+ """Get statistics for all agents"""
143
+ return {
144
+ "current_agent": self.current_agent_name,
145
+ "agents": {
146
+ name: agent.get_stats()
147
+ for name, agent in self.agents.items()
148
+ }
149
+ }
150
+
151
+ def register_agent(self, name: str, agent: BaseAgent) -> None:
152
+ """
153
+ Register custom agent
154
+
155
+ Args:
156
+ name: Agent name
157
+ agent: Agent instance
158
+ """
159
+ self.agents[name] = agent
160
+
161
+ def unregister_agent(self, name: str) -> bool:
162
+ """
163
+ Unregister agent
164
+
165
+ Args:
166
+ name: Agent name
167
+
168
+ Returns:
169
+ True if unregistered successfully
170
+ """
171
+ if name in ["build", "plan"]:
172
+ # Cannot unregister core agents
173
+ return False
174
+
175
+ if name in self.agents:
176
+ del self.agents[name]
177
+
178
+ # Switch to build if current agent was removed
179
+ if self.current_agent_name == name:
180
+ self.current_agent_name = "build"
181
+
182
+ return True
183
+
184
+ return False
185
+
186
+ def __repr__(self) -> str:
187
+ return f"AgentManager(current={self.current_agent_name}, agents={list(self.agents.keys())})"
@@ -0,0 +1,118 @@
1
+ """
2
+ Base Agent Interface
3
+
4
+ Defines the common interface for all groknroll agents.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from pathlib import Path
9
+ from typing import Dict, Any, Optional, List
10
+ from dataclasses import dataclass
11
+ from enum import Enum
12
+
13
+
14
+ class AgentCapability(Enum):
15
+ """Agent capabilities"""
16
+ READ_FILES = "read_files"
17
+ WRITE_FILES = "write_files"
18
+ EDIT_FILES = "edit_files"
19
+ DELETE_FILES = "delete_files"
20
+ EXECUTE_BASH = "execute_bash"
21
+ GIT_OPERATIONS = "git_operations"
22
+ ANALYZE_CODE = "analyze_code"
23
+ SEARCH_CODE = "search_code"
24
+
25
+
26
+ @dataclass
27
+ class AgentConfig:
28
+ """Configuration for an agent"""
29
+ name: str
30
+ description: str
31
+ capabilities: List[AgentCapability]
32
+ model: str = "gpt-4o-mini"
33
+ max_cost: float = 5.0
34
+ timeout: int = 300
35
+ verbose: bool = False
36
+
37
+
38
+ @dataclass
39
+ class AgentResponse:
40
+ """Response from agent execution"""
41
+ success: bool
42
+ message: str
43
+ agent_name: str
44
+ task: str
45
+ cost: float = 0.0
46
+ time: float = 0.0
47
+ metadata: Optional[Dict[str, Any]] = None
48
+
49
+
50
+ class BaseAgent(ABC):
51
+ """
52
+ Base class for all groknroll agents
53
+
54
+ Agents have different capabilities and permission levels.
55
+ """
56
+
57
+ def __init__(self, config: AgentConfig, project_path: Path):
58
+ """
59
+ Initialize agent
60
+
61
+ Args:
62
+ config: Agent configuration
63
+ project_path: Project root path
64
+ """
65
+ self.config = config
66
+ self.project_path = project_path
67
+ self.execution_history: List[AgentResponse] = []
68
+
69
+ @abstractmethod
70
+ def execute(self, task: str, context: Optional[Dict[str, Any]] = None) -> AgentResponse:
71
+ """
72
+ Execute task
73
+
74
+ Args:
75
+ task: Task description
76
+ context: Additional context
77
+
78
+ Returns:
79
+ AgentResponse
80
+ """
81
+ pass
82
+
83
+ def can(self, capability: AgentCapability) -> bool:
84
+ """Check if agent has capability"""
85
+ return capability in self.config.capabilities
86
+
87
+ def require(self, capability: AgentCapability) -> None:
88
+ """Require capability (raises error if not available)"""
89
+ if not self.can(capability):
90
+ raise PermissionError(
91
+ f"Agent '{self.config.name}' does not have capability: {capability.value}"
92
+ )
93
+
94
+ def get_capabilities(self) -> List[str]:
95
+ """Get list of capability names"""
96
+ return [cap.value for cap in self.config.capabilities]
97
+
98
+ def get_stats(self) -> Dict[str, Any]:
99
+ """Get agent statistics"""
100
+ successful = [r for r in self.execution_history if r.success]
101
+ failed = [r for r in self.execution_history if not r.success]
102
+
103
+ return {
104
+ "name": self.config.name,
105
+ "total_executions": len(self.execution_history),
106
+ "successful": len(successful),
107
+ "failed": len(failed),
108
+ "total_cost": sum(r.cost for r in self.execution_history),
109
+ "total_time": sum(r.time for r in self.execution_history),
110
+ "capabilities": self.get_capabilities()
111
+ }
112
+
113
+ def _log_execution(self, response: AgentResponse) -> None:
114
+ """Log execution to history"""
115
+ self.execution_history.append(response)
116
+
117
+ def __repr__(self) -> str:
118
+ return f"{self.__class__.__name__}(name={self.config.name})"
@@ -0,0 +1,231 @@
1
+ """
2
+ Build Agent
3
+
4
+ Full-access agent that can read, write, execute bash, and use git.
5
+ This is the primary agent for implementing features and making changes.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Dict, Any, Optional
10
+ import time
11
+
12
+ from groknroll.agents.base_agent import (
13
+ BaseAgent, AgentConfig, AgentCapability, AgentResponse
14
+ )
15
+ from groknroll.core.rlm_integration import RLMIntegration, RLMConfig
16
+ from groknroll.operations.file_ops import FileOperations
17
+ from groknroll.operations.bash_ops import BashOperations
18
+ from groknroll.operations.git_ops import GitOperations
19
+
20
+
21
+ class BuildAgent(BaseAgent):
22
+ """
23
+ Build Agent - Full Access
24
+
25
+ Capabilities:
26
+ - Read/write/edit/delete files
27
+ - Execute bash commands
28
+ - Git operations
29
+ - Code analysis
30
+ - Code search
31
+
32
+ Use for:
33
+ - Implementing new features
34
+ - Fixing bugs
35
+ - Refactoring code
36
+ - Running tests
37
+ - Building projects
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ project_path: Path,
43
+ model: str = "gpt-4o-mini",
44
+ max_cost: float = 5.0,
45
+ timeout: int = 300
46
+ ):
47
+ """
48
+ Initialize build agent
49
+
50
+ Args:
51
+ project_path: Project root path
52
+ model: LLM model to use
53
+ max_cost: Maximum cost per execution
54
+ timeout: Timeout in seconds
55
+ """
56
+ # Build agent has all capabilities
57
+ config = AgentConfig(
58
+ name="build",
59
+ description="Full-access agent for building and modifying code",
60
+ capabilities=[
61
+ AgentCapability.READ_FILES,
62
+ AgentCapability.WRITE_FILES,
63
+ AgentCapability.EDIT_FILES,
64
+ AgentCapability.DELETE_FILES,
65
+ AgentCapability.EXECUTE_BASH,
66
+ AgentCapability.GIT_OPERATIONS,
67
+ AgentCapability.ANALYZE_CODE,
68
+ AgentCapability.SEARCH_CODE,
69
+ ],
70
+ model=model,
71
+ max_cost=max_cost,
72
+ timeout=timeout
73
+ )
74
+
75
+ super().__init__(config, project_path)
76
+
77
+ # Initialize operations
78
+ self.file_ops = FileOperations(project_path)
79
+ self.bash_ops = BashOperations(project_path)
80
+
81
+ # Initialize git ops (may fail if not a git repo)
82
+ try:
83
+ self.git_ops = GitOperations(project_path)
84
+ except RuntimeError:
85
+ self.git_ops = None
86
+
87
+ # Initialize RLM
88
+ rlm_config = RLMConfig(
89
+ model=model,
90
+ max_cost=max_cost,
91
+ timeout_seconds=timeout
92
+ )
93
+ self.rlm = RLMIntegration(rlm_config)
94
+
95
+ def execute(self, task: str, context: Optional[Dict[str, Any]] = None) -> AgentResponse:
96
+ """
97
+ Execute build task
98
+
99
+ Args:
100
+ task: Task description
101
+ context: Additional context
102
+
103
+ Returns:
104
+ AgentResponse
105
+ """
106
+ start_time = time.time()
107
+
108
+ try:
109
+ # Prepare context with available operations
110
+ exec_context = {
111
+ "agent": "build",
112
+ "capabilities": self.get_capabilities(),
113
+ "project_path": str(self.project_path),
114
+ "operations": {
115
+ "file": "Available: read, write, edit, delete",
116
+ "bash": "Available: execute commands",
117
+ "git": "Available: status, add, commit, push, etc." if self.git_ops else "Not available"
118
+ },
119
+ **(context or {})
120
+ }
121
+
122
+ # Add instructions for using operations
123
+ enhanced_task = f"""Task: {task}
124
+
125
+ You are the BUILD agent with full access to:
126
+ - File operations (read, write, edit, delete)
127
+ - Bash execution
128
+ - Git operations
129
+
130
+ You can use RLM to execute Python code that calls these operations.
131
+
132
+ Available in context:
133
+ - file_ops: FileOperations instance
134
+ - bash_ops: BashOperations instance
135
+ - git_ops: GitOperations instance (or None)
136
+
137
+ Example usage in RLM code:
138
+ ```python
139
+ # Read file
140
+ result = file_ops.read_file(Path("example.py"))
141
+ if result.success:
142
+ content = result.message
143
+
144
+ # Write file
145
+ result = file_ops.write_file(Path("new.py"), "print('hello')")
146
+
147
+ # Execute bash
148
+ result = bash_ops.execute("ls -la")
149
+ print(result.stdout)
150
+
151
+ # Git operations
152
+ status = git_ops.status()
153
+ print(f"Branch: {{status.branch}}")
154
+ ```
155
+
156
+ Execute the task using these operations.
157
+ """
158
+
159
+ # Execute with RLM
160
+ result = self.rlm.complete(
161
+ task=enhanced_task,
162
+ context=exec_context
163
+ )
164
+
165
+ elapsed_time = time.time() - start_time
166
+
167
+ response = AgentResponse(
168
+ success=result.success,
169
+ message=result.response if result.success else result.error or "Unknown error",
170
+ agent_name=self.config.name,
171
+ task=task,
172
+ cost=result.total_cost,
173
+ time=elapsed_time,
174
+ metadata={
175
+ "iterations": result.iterations,
176
+ "rlm_success": result.success
177
+ }
178
+ )
179
+
180
+ self._log_execution(response)
181
+ return response
182
+
183
+ except Exception as e:
184
+ elapsed_time = time.time() - start_time
185
+
186
+ response = AgentResponse(
187
+ success=False,
188
+ message=f"Build agent error: {e}",
189
+ agent_name=self.config.name,
190
+ task=task,
191
+ time=elapsed_time
192
+ )
193
+
194
+ self._log_execution(response)
195
+ return response
196
+
197
+ # =========================================================================
198
+ # Convenience Methods (Direct Operations)
199
+ # =========================================================================
200
+
201
+ def read_file(self, path: Path) -> str:
202
+ """Read file (convenience method)"""
203
+ self.require(AgentCapability.READ_FILES)
204
+ result = self.file_ops.read_file(path)
205
+ if not result.success:
206
+ raise RuntimeError(result.message)
207
+ return result.message
208
+
209
+ def write_file(self, path: Path, content: str) -> None:
210
+ """Write file (convenience method)"""
211
+ self.require(AgentCapability.WRITE_FILES)
212
+ result = self.file_ops.write_file(path, content, overwrite=True)
213
+ if not result.success:
214
+ raise RuntimeError(result.message)
215
+
216
+ def run_command(self, command: str) -> str:
217
+ """Run bash command (convenience method)"""
218
+ self.require(AgentCapability.EXECUTE_BASH)
219
+ result = self.bash_ops.execute(command)
220
+ if not result.success:
221
+ raise RuntimeError(result.stderr)
222
+ return result.stdout
223
+
224
+ def git_status(self) -> str:
225
+ """Get git status (convenience method)"""
226
+ self.require(AgentCapability.GIT_OPERATIONS)
227
+ if not self.git_ops:
228
+ raise RuntimeError("Git not available")
229
+
230
+ status = self.git_ops.status()
231
+ return f"Branch: {status.branch}, Staged: {len(status.staged)}, Unstaged: {len(status.unstaged)}"