kubrick-cli 0.1.2__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 (35) hide show
  1. kubrick_cli-0.1.2/LICENSE +21 -0
  2. kubrick_cli-0.1.2/PKG-INFO +86 -0
  3. kubrick_cli-0.1.2/README.md +64 -0
  4. kubrick_cli-0.1.2/kubrick_cli/__init__.py +47 -0
  5. kubrick_cli-0.1.2/kubrick_cli/agent_loop.py +307 -0
  6. kubrick_cli-0.1.2/kubrick_cli/classifier.py +198 -0
  7. kubrick_cli-0.1.2/kubrick_cli/config.py +248 -0
  8. kubrick_cli-0.1.2/kubrick_cli/display.py +165 -0
  9. kubrick_cli-0.1.2/kubrick_cli/execution_strategy.py +197 -0
  10. kubrick_cli-0.1.2/kubrick_cli/main.py +797 -0
  11. kubrick_cli-0.1.2/kubrick_cli/planning.py +328 -0
  12. kubrick_cli-0.1.2/kubrick_cli/progress.py +172 -0
  13. kubrick_cli-0.1.2/kubrick_cli/providers/__init__.py +6 -0
  14. kubrick_cli-0.1.2/kubrick_cli/providers/anthropic_provider.py +224 -0
  15. kubrick_cli-0.1.2/kubrick_cli/providers/base.py +137 -0
  16. kubrick_cli-0.1.2/kubrick_cli/providers/factory.py +183 -0
  17. kubrick_cli-0.1.2/kubrick_cli/providers/openai_provider.py +190 -0
  18. kubrick_cli-0.1.2/kubrick_cli/providers/triton_provider.py +96 -0
  19. kubrick_cli-0.1.2/kubrick_cli/safety.py +216 -0
  20. kubrick_cli-0.1.2/kubrick_cli/scheduler.py +197 -0
  21. kubrick_cli-0.1.2/kubrick_cli/setup_wizard.py +172 -0
  22. kubrick_cli-0.1.2/kubrick_cli/tools.py +407 -0
  23. kubrick_cli-0.1.2/kubrick_cli/triton_client.py +192 -0
  24. kubrick_cli-0.1.2/kubrick_cli.egg-info/PKG-INFO +86 -0
  25. kubrick_cli-0.1.2/kubrick_cli.egg-info/SOURCES.txt +33 -0
  26. kubrick_cli-0.1.2/kubrick_cli.egg-info/dependency_links.txt +1 -0
  27. kubrick_cli-0.1.2/kubrick_cli.egg-info/entry_points.txt +2 -0
  28. kubrick_cli-0.1.2/kubrick_cli.egg-info/requires.txt +9 -0
  29. kubrick_cli-0.1.2/kubrick_cli.egg-info/top_level.txt +1 -0
  30. kubrick_cli-0.1.2/pyproject.toml +40 -0
  31. kubrick_cli-0.1.2/setup.cfg +4 -0
  32. kubrick_cli-0.1.2/tests/test_client.py +74 -0
  33. kubrick_cli-0.1.2/tests/test_config.py +101 -0
  34. kubrick_cli-0.1.2/tests/test_tool_calling.py +126 -0
  35. kubrick_cli-0.1.2/tests/test_triton.py +127 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026] [Russell Land]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: kubrick-cli
3
+ Version: 0.1.2
4
+ Summary: A CLI tool for AI-assisted coding using Triton LLM backend
5
+ Author-email: Russell Land <rcland12@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/rcland12/kubrick-cli
8
+ Project-URL: Repository, https://github.com/rcland12/kubrick-cli
9
+ Project-URL: Issues, https://github.com/rcland12/kubrick-cli/issues
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: rich>=13.0.0
14
+ Requires-Dist: prompt_toolkit>=3.0.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
17
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
18
+ Requires-Dist: black>=22.0.0; extra == "dev"
19
+ Requires-Dist: build>=1.0.0; extra == "dev"
20
+ Requires-Dist: twine>=5.0.0; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # Kubrick CLI
24
+
25
+ An AI-assisted coding CLI tool powered by your own Triton LLM backend. Like Claude Code, but self-hosted.
26
+
27
+ ## Installation
28
+
29
+ ### Local Installation
30
+
31
+ ```bash
32
+ pip install -e .
33
+ kubrick
34
+ ```
35
+
36
+ ### Docker
37
+
38
+ ```bash
39
+ cd /path/to/your/project
40
+ docker run --rm -it \
41
+ --network host \
42
+ -v ${HOME}:/home/kubrick \
43
+ -v ${PWD}:/workspace \
44
+ -v /etc/localtime:/etc/localtime:ro \
45
+ -v /etc/timezone:/etc/timezone:ro \
46
+ rcland12/kubrick-cli
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ```bash
52
+ # Basic usage
53
+ kubrick
54
+
55
+ # Custom Triton server
56
+ kubrick --triton-url my-server:8000
57
+
58
+ # Load previous conversation
59
+ kubrick --load 20240118_143022
60
+ ```
61
+
62
+ ## Requirements
63
+
64
+ - Python 3.8+
65
+ - Triton Inference Server with streaming LLM model
66
+ - Default endpoint: `http://localhost:8000`
67
+ - Default model: `llm_decoupled`
68
+
69
+ ## Documentation
70
+
71
+ - **[WIKI.md](docs/WIKI.md)** - Complete features, commands, and usage guide
72
+ - **[TRITON.md](docs/TRITON.md)** - Triton backend setup and requirements
73
+ - **[DOCKER.md](docs/DOCKER.md)** - Docker setup and troubleshooting
74
+
75
+ ## Configuration
76
+
77
+ Config stored at `~/.kubrick/config.json`. Override with environment variables:
78
+
79
+ ```bash
80
+ export TRITON_URL=localhost:8000
81
+ export TRITON_MODEL_NAME=llm_decoupled
82
+ ```
83
+
84
+ ## License
85
+
86
+ MIT License - See [LICENSE](LICENSE)
@@ -0,0 +1,64 @@
1
+ # Kubrick CLI
2
+
3
+ An AI-assisted coding CLI tool powered by your own Triton LLM backend. Like Claude Code, but self-hosted.
4
+
5
+ ## Installation
6
+
7
+ ### Local Installation
8
+
9
+ ```bash
10
+ pip install -e .
11
+ kubrick
12
+ ```
13
+
14
+ ### Docker
15
+
16
+ ```bash
17
+ cd /path/to/your/project
18
+ docker run --rm -it \
19
+ --network host \
20
+ -v ${HOME}:/home/kubrick \
21
+ -v ${PWD}:/workspace \
22
+ -v /etc/localtime:/etc/localtime:ro \
23
+ -v /etc/timezone:/etc/timezone:ro \
24
+ rcland12/kubrick-cli
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ # Basic usage
31
+ kubrick
32
+
33
+ # Custom Triton server
34
+ kubrick --triton-url my-server:8000
35
+
36
+ # Load previous conversation
37
+ kubrick --load 20240118_143022
38
+ ```
39
+
40
+ ## Requirements
41
+
42
+ - Python 3.8+
43
+ - Triton Inference Server with streaming LLM model
44
+ - Default endpoint: `http://localhost:8000`
45
+ - Default model: `llm_decoupled`
46
+
47
+ ## Documentation
48
+
49
+ - **[WIKI.md](docs/WIKI.md)** - Complete features, commands, and usage guide
50
+ - **[TRITON.md](docs/TRITON.md)** - Triton backend setup and requirements
51
+ - **[DOCKER.md](docs/DOCKER.md)** - Docker setup and troubleshooting
52
+
53
+ ## Configuration
54
+
55
+ Config stored at `~/.kubrick/config.json`. Override with environment variables:
56
+
57
+ ```bash
58
+ export TRITON_URL=localhost:8000
59
+ export TRITON_MODEL_NAME=llm_decoupled
60
+ ```
61
+
62
+ ## License
63
+
64
+ MIT License - See [LICENSE](LICENSE)
@@ -0,0 +1,47 @@
1
+ """Kubrick CLI - AI-assisted coding tool with agentic capabilities."""
2
+
3
+ __version__ = "0.2.0"
4
+
5
+ from .agent_loop import AgentLoop, CompletionDetector
6
+ from .classifier import TaskClassification, TaskClassifier
7
+ from .config import KubrickConfig
8
+ from .display import DisplayManager
9
+ from .execution_strategy import ExecutionConfig, ExecutionStrategy
10
+ from .main import KubrickCLI
11
+ from .planning import PlanningPhase
12
+ from .progress import ProgressTracker
13
+ from .providers.base import ProviderAdapter
14
+ from .providers.factory import ProviderFactory
15
+ from .providers.triton_provider import TritonProvider
16
+ from .providers.openai_provider import OpenAIProvider
17
+ from .providers.anthropic_provider import AnthropicProvider
18
+ from .safety import SafetyConfig, SafetyManager
19
+ from .scheduler import ToolScheduler
20
+ from .setup_wizard import SetupWizard
21
+ from .tools import ToolExecutor
22
+ from .triton_client import TritonLLMClient
23
+
24
+ __all__ = [
25
+ "AgentLoop",
26
+ "AnthropicProvider",
27
+ "CompletionDetector",
28
+ "DisplayManager",
29
+ "ExecutionConfig",
30
+ "ExecutionStrategy",
31
+ "KubrickCLI",
32
+ "KubrickConfig",
33
+ "OpenAIProvider",
34
+ "PlanningPhase",
35
+ "ProgressTracker",
36
+ "ProviderAdapter",
37
+ "ProviderFactory",
38
+ "SafetyConfig",
39
+ "SafetyManager",
40
+ "SetupWizard",
41
+ "TaskClassification",
42
+ "TaskClassifier",
43
+ "ToolExecutor",
44
+ "ToolScheduler",
45
+ "TritonLLMClient",
46
+ "TritonProvider",
47
+ ]
@@ -0,0 +1,307 @@
1
+ """Multi-step agentic execution loop with completion detection."""
2
+
3
+ import re
4
+ from typing import Dict, Iterator, List, Optional, Tuple
5
+
6
+ from rich.console import Console
7
+
8
+ console = Console()
9
+
10
+ # Import display manager (will be None if not available)
11
+ try:
12
+ from .display import DisplayManager
13
+ except ImportError:
14
+ DisplayManager = None
15
+
16
+ # Import scheduler (will be None if not available)
17
+ try:
18
+ from .scheduler import ToolScheduler
19
+ except ImportError:
20
+ ToolScheduler = None
21
+
22
+
23
+ class CompletionDetector:
24
+ """Detects when an agent has completed its task."""
25
+
26
+ # Explicit completion markers the agent can use
27
+ COMPLETION_MARKERS = [
28
+ "TASK_COMPLETE",
29
+ "PLAN_COMPLETE",
30
+ "[COMPLETE]",
31
+ "[DONE]",
32
+ ]
33
+
34
+ @staticmethod
35
+ def is_complete(
36
+ response_text: str,
37
+ has_tool_calls: bool,
38
+ iteration: int,
39
+ max_iterations: int,
40
+ ) -> Tuple[bool, str]:
41
+ """
42
+ Determine if the agent has completed its task.
43
+
44
+ Args:
45
+ response_text: The agent's response text
46
+ has_tool_calls: Whether the response contains tool calls
47
+ iteration: Current iteration number
48
+ max_iterations: Maximum allowed iterations
49
+
50
+ Returns:
51
+ Tuple of (is_complete, reason)
52
+ """
53
+ # Check for explicit completion markers
54
+ for marker in CompletionDetector.COMPLETION_MARKERS:
55
+ if marker in response_text:
56
+ return True, f"explicit_marker:{marker}"
57
+
58
+ # Max iterations reached (fallback safety)
59
+ if iteration >= max_iterations:
60
+ return True, "max_iterations_reached"
61
+
62
+ # If no tool calls and response looks conclusive
63
+ if not has_tool_calls:
64
+ # Check if response appears to be a final summary/conclusion
65
+ if CompletionDetector._looks_conclusive(response_text):
66
+ return True, "conclusive_response"
67
+
68
+ # Otherwise, continue
69
+ return False, "continuing"
70
+
71
+ @staticmethod
72
+ def _looks_conclusive(text: str) -> bool:
73
+ """
74
+ Check if text looks like a conclusive response.
75
+
76
+ This is a heuristic to detect when the agent is done without
77
+ explicitly saying so.
78
+ """
79
+ text_lower = text.lower()
80
+
81
+ # Look for summary/conclusion indicators
82
+ conclusive_patterns = [
83
+ r"\b(done|completed|finished|ready)\b",
84
+ r"\b(successfully|all set|good to go)\b",
85
+ r"\bhere(?:'s| is) (?:the |a )?(?:summary|result)",
86
+ r"\b(?:task|work|changes) (?:is |are )?(?:complete|done|finished)",
87
+ r"\blet me know if you need",
88
+ r"\bthat(?:'s| should do it)",
89
+ r"\beverything(?:'s| is) (?:set|ready|done)",
90
+ ]
91
+
92
+ for pattern in conclusive_patterns:
93
+ if re.search(pattern, text_lower):
94
+ return True
95
+
96
+ return False
97
+
98
+
99
+ class AgentLoop:
100
+ """
101
+ Multi-step agentic execution loop.
102
+
103
+ Runs iteratively until the task is complete or max iterations reached.
104
+ """
105
+
106
+ def __init__(
107
+ self,
108
+ llm_client,
109
+ tool_executor,
110
+ max_iterations: int = 15,
111
+ max_tools_per_turn: int = 5,
112
+ timeout_seconds: int = 600,
113
+ stream_options: Dict = None,
114
+ display_manager=None,
115
+ tool_scheduler=None,
116
+ ):
117
+ """
118
+ Initialize the agent loop.
119
+
120
+ Args:
121
+ llm_client: LLM client instance (e.g., TritonLLMClient)
122
+ tool_executor: Tool executor instance
123
+ max_iterations: Maximum number of iterations
124
+ max_tools_per_turn: Maximum tools per turn
125
+ timeout_seconds: Total timeout in seconds
126
+ stream_options: Optional streaming options
127
+ display_manager: Optional DisplayManager instance for natural language display
128
+ tool_scheduler: Optional ToolScheduler for parallel execution
129
+ """
130
+ self.llm_client = llm_client
131
+ self.tool_executor = tool_executor
132
+ self.max_iterations = max_iterations
133
+ self.max_tools_per_turn = max_tools_per_turn
134
+ self.timeout_seconds = timeout_seconds
135
+ self.stream_options = stream_options or {}
136
+ self.display_manager = display_manager
137
+ self.tool_scheduler = tool_scheduler
138
+
139
+ def run(
140
+ self,
141
+ messages: List[Dict],
142
+ tool_parser,
143
+ display_callback=None,
144
+ ) -> Dict:
145
+ """
146
+ Run the agentic loop.
147
+
148
+ Args:
149
+ messages: Conversation messages (will be modified in-place)
150
+ tool_parser: Function to parse tool calls from text
151
+ display_callback: Optional callback for displaying streaming response
152
+
153
+ Returns:
154
+ Dict with execution results and metadata
155
+ """
156
+ iteration = 0
157
+ total_tool_calls = 0
158
+
159
+ while iteration < self.max_iterations:
160
+ iteration += 1
161
+
162
+ console.print(
163
+ f"\n[dim]→ Agent iteration {iteration}/{self.max_iterations}[/dim]"
164
+ )
165
+
166
+ # Get LLM response
167
+ console.print("[bold cyan]Assistant:[/bold cyan]")
168
+ chunks = []
169
+
170
+ try:
171
+ for chunk in self.llm_client.generate_streaming(
172
+ messages, stream_options=self.stream_options
173
+ ):
174
+ console.print(chunk, end="")
175
+ chunks.append(chunk)
176
+
177
+ console.print("\n")
178
+
179
+ except Exception as e:
180
+ console.print(f"\n[red]Error during LLM generation: {e}[/red]")
181
+ return {
182
+ "success": False,
183
+ "error": str(e),
184
+ "iterations": iteration,
185
+ "tool_calls": total_tool_calls,
186
+ }
187
+
188
+ # Get full response
189
+ response_text = "".join(chunks)
190
+
191
+ # Add to conversation
192
+ messages.append({"role": "assistant", "content": response_text})
193
+
194
+ # Display with custom callback if provided
195
+ if display_callback:
196
+ display_callback(response_text)
197
+
198
+ # Parse tool calls
199
+ tool_calls = tool_parser(response_text)
200
+
201
+ # Check completion
202
+ is_complete, reason = CompletionDetector.is_complete(
203
+ response_text=response_text,
204
+ has_tool_calls=len(tool_calls) > 0,
205
+ iteration=iteration,
206
+ max_iterations=self.max_iterations,
207
+ )
208
+
209
+ if is_complete:
210
+ console.print(f"\n[green]✓ Task complete ({reason})[/green]")
211
+ return {
212
+ "success": True,
213
+ "completion_reason": reason,
214
+ "iterations": iteration,
215
+ "tool_calls": total_tool_calls,
216
+ }
217
+
218
+ # Execute tool calls if any
219
+ if tool_calls:
220
+ # Enforce max tools per turn
221
+ if len(tool_calls) > self.max_tools_per_turn:
222
+ console.print(
223
+ f"[yellow]⚠ Too many tool calls ({len(tool_calls)}), "
224
+ f"limiting to {self.max_tools_per_turn}[/yellow]"
225
+ )
226
+ tool_calls = tool_calls[: self.max_tools_per_turn]
227
+
228
+ console.print(
229
+ f"\n[yellow]Executing {len(tool_calls)} tool(s)...[/yellow]\n"
230
+ )
231
+
232
+ # Execute tools (with or without scheduler)
233
+ if self.tool_scheduler and len(tool_calls) > 1:
234
+ # Use scheduler for parallel execution
235
+ execution_results = self.tool_scheduler.execute_tools(
236
+ tool_calls
237
+ )
238
+ else:
239
+ # Execute sequentially
240
+ execution_results = [
241
+ self.tool_executor.execute(tool_name, params)
242
+ for tool_name, params in tool_calls
243
+ ]
244
+
245
+ # Display and collect results
246
+ tool_results = []
247
+ for (tool_name, parameters), result in zip(
248
+ tool_calls, execution_results
249
+ ):
250
+ # Display tool call and result
251
+ if self.display_manager:
252
+ self.display_manager.display_tool_call(
253
+ tool_name, parameters
254
+ )
255
+ self.display_manager.display_tool_result(
256
+ tool_name, result, result["success"]
257
+ )
258
+ else:
259
+ console.print(f"[cyan]→ Called {tool_name}[/cyan]")
260
+ if result["success"]:
261
+ console.print(
262
+ f"[green]✓ {tool_name} succeeded[/green]"
263
+ )
264
+ else:
265
+ console.print(
266
+ f"[red]✗ {tool_name} failed: {result['error']}[/red]"
267
+ )
268
+
269
+ # Add to results
270
+ if result["success"]:
271
+ tool_results.append(
272
+ f"Tool: {tool_name}\nResult: {result['result']}"
273
+ )
274
+ else:
275
+ tool_results.append(
276
+ f"Tool: {tool_name}\nError: {result['error']}"
277
+ )
278
+
279
+ total_tool_calls += 1
280
+
281
+ # Add tool results to conversation
282
+ tool_results_text = "\n\n".join(tool_results)
283
+ messages.append(
284
+ {
285
+ "role": "user",
286
+ "content": f"Tool execution results:\n\n{tool_results_text}",
287
+ }
288
+ )
289
+
290
+ # Continue to next iteration
291
+ continue
292
+
293
+ # If no tool calls and not complete, something might be wrong
294
+ console.print(
295
+ "[yellow]⚠ No tool calls and task not marked complete. Continuing...[/yellow]"
296
+ )
297
+
298
+ # Max iterations reached
299
+ console.print(
300
+ f"\n[yellow]⚠ Max iterations ({self.max_iterations}) reached[/yellow]"
301
+ )
302
+ return {
303
+ "success": True,
304
+ "completion_reason": "max_iterations",
305
+ "iterations": iteration,
306
+ "tool_calls": total_tool_calls,
307
+ }