kubrick-cli 0.1.2__tar.gz → 0.1.4__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.
- kubrick_cli-0.1.4/PKG-INFO +137 -0
- kubrick_cli-0.1.4/README.md +113 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/agent_loop.py +4 -37
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/classifier.py +0 -4
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/config.py +14 -15
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/display.py +5 -16
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/execution_strategy.py +4 -6
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/main.py +119 -110
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/planning.py +15 -24
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/progress.py +3 -13
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/anthropic_provider.py +0 -15
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/base.py +0 -1
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/factory.py +5 -27
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/openai_provider.py +0 -9
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/safety.py +4 -16
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/scheduler.py +5 -19
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/setup_wizard.py +0 -11
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/tools.py +3 -10
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/triton_client.py +10 -25
- kubrick_cli-0.1.4/kubrick_cli.egg-info/PKG-INFO +137 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli.egg-info/SOURCES.txt +4 -2
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli.egg-info/requires.txt +2 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/pyproject.toml +3 -1
- kubrick_cli-0.1.4/tests/test_completion_detector.py +345 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/tests/test_config.py +47 -18
- kubrick_cli-0.1.4/tests/test_safety.py +294 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/tests/test_tool_calling.py +10 -10
- kubrick_cli-0.1.4/tests/test_tool_executor.py +265 -0
- kubrick_cli-0.1.4/tests/test_triton_client_unit.py +464 -0
- kubrick_cli-0.1.2/PKG-INFO +0 -86
- kubrick_cli-0.1.2/README.md +0 -64
- kubrick_cli-0.1.2/kubrick_cli.egg-info/PKG-INFO +0 -86
- kubrick_cli-0.1.2/tests/test_client.py +0 -74
- kubrick_cli-0.1.2/tests/test_triton.py +0 -127
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/LICENSE +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/__init__.py +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/__init__.py +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli/providers/triton_provider.py +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli.egg-info/dependency_links.txt +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli.egg-info/entry_points.txt +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/kubrick_cli.egg-info/top_level.txt +0 -0
- {kubrick_cli-0.1.2 → kubrick_cli-0.1.4}/setup.cfg +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kubrick-cli
|
|
3
|
+
Version: 0.1.4
|
|
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: pytest-mock>=3.10.0; extra == "dev"
|
|
19
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
20
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# Kubrick CLI
|
|
26
|
+
|
|
27
|
+
An AI-assisted coding CLI tool powered by your own Triton LLM backend. Like Claude Code, but self-hosted.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### Local Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install -e .
|
|
35
|
+
kubrick
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Docker
|
|
39
|
+
|
|
40
|
+
Available from Docker Hub or GitHub Container Registry:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd /path/to/your/project
|
|
44
|
+
|
|
45
|
+
# From Docker Hub
|
|
46
|
+
docker run --rm -it \
|
|
47
|
+
--network host \
|
|
48
|
+
-v ${HOME}:/home/kubrick \
|
|
49
|
+
-v ${PWD}:/workspace \
|
|
50
|
+
-v /etc/localtime:/etc/localtime:ro \
|
|
51
|
+
-v /etc/timezone:/etc/timezone:ro \
|
|
52
|
+
rcland12/kubrick-cli
|
|
53
|
+
|
|
54
|
+
# From GitHub Container Registry
|
|
55
|
+
docker run --rm -it \
|
|
56
|
+
--network host \
|
|
57
|
+
-v ${HOME}:/home/kubrick \
|
|
58
|
+
-v ${PWD}:/workspace \
|
|
59
|
+
-v /etc/localtime:/etc/localtime:ro \
|
|
60
|
+
-v /etc/timezone:/etc/timezone:ro \
|
|
61
|
+
ghcr.io/rcland12/kubrick-cli
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Basic usage
|
|
68
|
+
kubrick
|
|
69
|
+
|
|
70
|
+
# Custom Triton server
|
|
71
|
+
kubrick --triton-url my-server:8000
|
|
72
|
+
|
|
73
|
+
# Load previous conversation
|
|
74
|
+
kubrick --load 20240118_143022
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Requirements
|
|
78
|
+
|
|
79
|
+
- Python 3.8+
|
|
80
|
+
- LLM Provider (choose one):
|
|
81
|
+
- **Triton Inference Server** with streaming LLM model (default: `http://localhost:8000`)
|
|
82
|
+
- **OpenAI API** (GPT-4, GPT-3.5-turbo, etc.)
|
|
83
|
+
- **Anthropic API** (Claude models)
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
- **[WIKI.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/WIKI.md)** - Complete features, commands, and usage guide
|
|
88
|
+
- **[PROVIDERS.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/PROVIDERS.md)** - Multi-provider setup (Triton, OpenAI, Anthropic)
|
|
89
|
+
- **[TRITON.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TRITON.md)** - Triton backend setup and requirements
|
|
90
|
+
- **[TESTING.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TESTING.md)** - Testing guide and CI/CD setup
|
|
91
|
+
- **[DOCKER.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/DOCKER.md)** - Docker setup and troubleshooting
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
Config stored at `~/.kubrick/config.json`. Override with environment variables:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export TRITON_URL=localhost:8000
|
|
99
|
+
export TRITON_MODEL_NAME=llm_decoupled
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
### Install Development Dependencies
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pip install -e ".[dev]"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Run Tests
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Run all tests
|
|
114
|
+
pytest
|
|
115
|
+
|
|
116
|
+
# Run with coverage
|
|
117
|
+
pytest --cov=kubrick_cli --cov-report=term-missing
|
|
118
|
+
|
|
119
|
+
# Run specific test file
|
|
120
|
+
pytest tests/test_tool_executor.py -v
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
See [TESTING.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TESTING.md) for detailed testing documentation.
|
|
124
|
+
|
|
125
|
+
### Code Quality
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Format code
|
|
129
|
+
black kubrick_cli tests
|
|
130
|
+
|
|
131
|
+
# Check linting
|
|
132
|
+
flake8 kubrick_cli
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT License - See [LICENSE](LICENSE)
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
Available from Docker Hub or GitHub Container Registry:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cd /path/to/your/project
|
|
20
|
+
|
|
21
|
+
# From Docker Hub
|
|
22
|
+
docker run --rm -it \
|
|
23
|
+
--network host \
|
|
24
|
+
-v ${HOME}:/home/kubrick \
|
|
25
|
+
-v ${PWD}:/workspace \
|
|
26
|
+
-v /etc/localtime:/etc/localtime:ro \
|
|
27
|
+
-v /etc/timezone:/etc/timezone:ro \
|
|
28
|
+
rcland12/kubrick-cli
|
|
29
|
+
|
|
30
|
+
# From GitHub Container Registry
|
|
31
|
+
docker run --rm -it \
|
|
32
|
+
--network host \
|
|
33
|
+
-v ${HOME}:/home/kubrick \
|
|
34
|
+
-v ${PWD}:/workspace \
|
|
35
|
+
-v /etc/localtime:/etc/localtime:ro \
|
|
36
|
+
-v /etc/timezone:/etc/timezone:ro \
|
|
37
|
+
ghcr.io/rcland12/kubrick-cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Basic usage
|
|
44
|
+
kubrick
|
|
45
|
+
|
|
46
|
+
# Custom Triton server
|
|
47
|
+
kubrick --triton-url my-server:8000
|
|
48
|
+
|
|
49
|
+
# Load previous conversation
|
|
50
|
+
kubrick --load 20240118_143022
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Python 3.8+
|
|
56
|
+
- LLM Provider (choose one):
|
|
57
|
+
- **Triton Inference Server** with streaming LLM model (default: `http://localhost:8000`)
|
|
58
|
+
- **OpenAI API** (GPT-4, GPT-3.5-turbo, etc.)
|
|
59
|
+
- **Anthropic API** (Claude models)
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
- **[WIKI.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/WIKI.md)** - Complete features, commands, and usage guide
|
|
64
|
+
- **[PROVIDERS.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/PROVIDERS.md)** - Multi-provider setup (Triton, OpenAI, Anthropic)
|
|
65
|
+
- **[TRITON.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TRITON.md)** - Triton backend setup and requirements
|
|
66
|
+
- **[TESTING.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TESTING.md)** - Testing guide and CI/CD setup
|
|
67
|
+
- **[DOCKER.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/DOCKER.md)** - Docker setup and troubleshooting
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
Config stored at `~/.kubrick/config.json`. Override with environment variables:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
export TRITON_URL=localhost:8000
|
|
75
|
+
export TRITON_MODEL_NAME=llm_decoupled
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
### Install Development Dependencies
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install -e ".[dev]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Run Tests
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Run all tests
|
|
90
|
+
pytest
|
|
91
|
+
|
|
92
|
+
# Run with coverage
|
|
93
|
+
pytest --cov=kubrick_cli --cov-report=term-missing
|
|
94
|
+
|
|
95
|
+
# Run specific test file
|
|
96
|
+
pytest tests/test_tool_executor.py -v
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See [TESTING.md](https://github.com/rcland12/kubrick-cli/blob/master/docs/TESTING.md) for detailed testing documentation.
|
|
100
|
+
|
|
101
|
+
### Code Quality
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Format code
|
|
105
|
+
black kubrick_cli tests
|
|
106
|
+
|
|
107
|
+
# Check linting
|
|
108
|
+
flake8 kubrick_cli
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT License - See [LICENSE](LICENSE)
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"""Multi-step agentic execution loop with completion detection."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from typing import Dict,
|
|
4
|
+
from typing import Dict, List, Tuple
|
|
5
5
|
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
|
|
8
8
|
console = Console()
|
|
9
9
|
|
|
10
|
-
# Import display manager (will be None if not available)
|
|
11
10
|
try:
|
|
12
11
|
from .display import DisplayManager
|
|
13
12
|
except ImportError:
|
|
14
13
|
DisplayManager = None
|
|
15
14
|
|
|
16
|
-
# Import scheduler (will be None if not available)
|
|
17
15
|
try:
|
|
18
16
|
from .scheduler import ToolScheduler
|
|
19
17
|
except ImportError:
|
|
@@ -23,7 +21,6 @@ except ImportError:
|
|
|
23
21
|
class CompletionDetector:
|
|
24
22
|
"""Detects when an agent has completed its task."""
|
|
25
23
|
|
|
26
|
-
# Explicit completion markers the agent can use
|
|
27
24
|
COMPLETION_MARKERS = [
|
|
28
25
|
"TASK_COMPLETE",
|
|
29
26
|
"PLAN_COMPLETE",
|
|
@@ -50,22 +47,17 @@ class CompletionDetector:
|
|
|
50
47
|
Returns:
|
|
51
48
|
Tuple of (is_complete, reason)
|
|
52
49
|
"""
|
|
53
|
-
# Check for explicit completion markers
|
|
54
50
|
for marker in CompletionDetector.COMPLETION_MARKERS:
|
|
55
51
|
if marker in response_text:
|
|
56
52
|
return True, f"explicit_marker:{marker}"
|
|
57
53
|
|
|
58
|
-
# Max iterations reached (fallback safety)
|
|
59
54
|
if iteration >= max_iterations:
|
|
60
55
|
return True, "max_iterations_reached"
|
|
61
56
|
|
|
62
|
-
# If no tool calls and response looks conclusive
|
|
63
57
|
if not has_tool_calls:
|
|
64
|
-
# Check if response appears to be a final summary/conclusion
|
|
65
58
|
if CompletionDetector._looks_conclusive(response_text):
|
|
66
59
|
return True, "conclusive_response"
|
|
67
60
|
|
|
68
|
-
# Otherwise, continue
|
|
69
61
|
return False, "continuing"
|
|
70
62
|
|
|
71
63
|
@staticmethod
|
|
@@ -78,7 +70,6 @@ class CompletionDetector:
|
|
|
78
70
|
"""
|
|
79
71
|
text_lower = text.lower()
|
|
80
72
|
|
|
81
|
-
# Look for summary/conclusion indicators
|
|
82
73
|
conclusive_patterns = [
|
|
83
74
|
r"\b(done|completed|finished|ready)\b",
|
|
84
75
|
r"\b(successfully|all set|good to go)\b",
|
|
@@ -163,7 +154,6 @@ class AgentLoop:
|
|
|
163
154
|
f"\n[dim]→ Agent iteration {iteration}/{self.max_iterations}[/dim]"
|
|
164
155
|
)
|
|
165
156
|
|
|
166
|
-
# Get LLM response
|
|
167
157
|
console.print("[bold cyan]Assistant:[/bold cyan]")
|
|
168
158
|
chunks = []
|
|
169
159
|
|
|
@@ -185,20 +175,15 @@ class AgentLoop:
|
|
|
185
175
|
"tool_calls": total_tool_calls,
|
|
186
176
|
}
|
|
187
177
|
|
|
188
|
-
# Get full response
|
|
189
178
|
response_text = "".join(chunks)
|
|
190
179
|
|
|
191
|
-
# Add to conversation
|
|
192
180
|
messages.append({"role": "assistant", "content": response_text})
|
|
193
181
|
|
|
194
|
-
# Display with custom callback if provided
|
|
195
182
|
if display_callback:
|
|
196
183
|
display_callback(response_text)
|
|
197
184
|
|
|
198
|
-
# Parse tool calls
|
|
199
185
|
tool_calls = tool_parser(response_text)
|
|
200
186
|
|
|
201
|
-
# Check completion
|
|
202
187
|
is_complete, reason = CompletionDetector.is_complete(
|
|
203
188
|
response_text=response_text,
|
|
204
189
|
has_tool_calls=len(tool_calls) > 0,
|
|
@@ -215,9 +200,7 @@ class AgentLoop:
|
|
|
215
200
|
"tool_calls": total_tool_calls,
|
|
216
201
|
}
|
|
217
202
|
|
|
218
|
-
# Execute tool calls if any
|
|
219
203
|
if tool_calls:
|
|
220
|
-
# Enforce max tools per turn
|
|
221
204
|
if len(tool_calls) > self.max_tools_per_turn:
|
|
222
205
|
console.print(
|
|
223
206
|
f"[yellow]⚠ Too many tool calls ({len(tool_calls)}), "
|
|
@@ -229,44 +212,32 @@ class AgentLoop:
|
|
|
229
212
|
f"\n[yellow]Executing {len(tool_calls)} tool(s)...[/yellow]\n"
|
|
230
213
|
)
|
|
231
214
|
|
|
232
|
-
# Execute tools (with or without scheduler)
|
|
233
215
|
if self.tool_scheduler and len(tool_calls) > 1:
|
|
234
|
-
|
|
235
|
-
execution_results = self.tool_scheduler.execute_tools(
|
|
236
|
-
tool_calls
|
|
237
|
-
)
|
|
216
|
+
execution_results = self.tool_scheduler.execute_tools(tool_calls)
|
|
238
217
|
else:
|
|
239
|
-
# Execute sequentially
|
|
240
218
|
execution_results = [
|
|
241
219
|
self.tool_executor.execute(tool_name, params)
|
|
242
220
|
for tool_name, params in tool_calls
|
|
243
221
|
]
|
|
244
222
|
|
|
245
|
-
# Display and collect results
|
|
246
223
|
tool_results = []
|
|
247
224
|
for (tool_name, parameters), result in zip(
|
|
248
225
|
tool_calls, execution_results
|
|
249
226
|
):
|
|
250
|
-
# Display tool call and result
|
|
251
227
|
if self.display_manager:
|
|
252
|
-
self.display_manager.display_tool_call(
|
|
253
|
-
tool_name, parameters
|
|
254
|
-
)
|
|
228
|
+
self.display_manager.display_tool_call(tool_name, parameters)
|
|
255
229
|
self.display_manager.display_tool_result(
|
|
256
230
|
tool_name, result, result["success"]
|
|
257
231
|
)
|
|
258
232
|
else:
|
|
259
233
|
console.print(f"[cyan]→ Called {tool_name}[/cyan]")
|
|
260
234
|
if result["success"]:
|
|
261
|
-
console.print(
|
|
262
|
-
f"[green]✓ {tool_name} succeeded[/green]"
|
|
263
|
-
)
|
|
235
|
+
console.print(f"[green]✓ {tool_name} succeeded[/green]")
|
|
264
236
|
else:
|
|
265
237
|
console.print(
|
|
266
238
|
f"[red]✗ {tool_name} failed: {result['error']}[/red]"
|
|
267
239
|
)
|
|
268
240
|
|
|
269
|
-
# Add to results
|
|
270
241
|
if result["success"]:
|
|
271
242
|
tool_results.append(
|
|
272
243
|
f"Tool: {tool_name}\nResult: {result['result']}"
|
|
@@ -278,7 +249,6 @@ class AgentLoop:
|
|
|
278
249
|
|
|
279
250
|
total_tool_calls += 1
|
|
280
251
|
|
|
281
|
-
# Add tool results to conversation
|
|
282
252
|
tool_results_text = "\n\n".join(tool_results)
|
|
283
253
|
messages.append(
|
|
284
254
|
{
|
|
@@ -287,15 +257,12 @@ class AgentLoop:
|
|
|
287
257
|
}
|
|
288
258
|
)
|
|
289
259
|
|
|
290
|
-
# Continue to next iteration
|
|
291
260
|
continue
|
|
292
261
|
|
|
293
|
-
# If no tool calls and not complete, something might be wrong
|
|
294
262
|
console.print(
|
|
295
263
|
"[yellow]⚠ No tool calls and task not marked complete. Continuing...[/yellow]"
|
|
296
264
|
)
|
|
297
265
|
|
|
298
|
-
# Max iterations reached
|
|
299
266
|
console.print(
|
|
300
267
|
f"\n[yellow]⚠ Max iterations ({self.max_iterations}) reached[/yellow]"
|
|
301
268
|
)
|
|
@@ -64,7 +64,6 @@ class TaskClassifier:
|
|
|
64
64
|
Returns:
|
|
65
65
|
TaskClassification object with detailed classification
|
|
66
66
|
"""
|
|
67
|
-
# Build classification prompt
|
|
68
67
|
classification_messages = [
|
|
69
68
|
{
|
|
70
69
|
"role": "system",
|
|
@@ -146,12 +145,10 @@ Respond with ONLY the JSON object, no other text.""",
|
|
|
146
145
|
]
|
|
147
146
|
|
|
148
147
|
try:
|
|
149
|
-
# Get classification from LLM (non-streaming for speed)
|
|
150
148
|
response = self.llm_client.generate(
|
|
151
149
|
classification_messages, stream_options={}
|
|
152
150
|
)
|
|
153
151
|
|
|
154
|
-
# Parse JSON response
|
|
155
152
|
json_start = response.find("{")
|
|
156
153
|
json_end = response.rfind("}") + 1
|
|
157
154
|
|
|
@@ -175,7 +172,6 @@ Respond with ONLY the JSON object, no other text.""",
|
|
|
175
172
|
requires_tools=requires_tools,
|
|
176
173
|
)
|
|
177
174
|
|
|
178
|
-
# Fallback: default to SIMPLE if parsing fails
|
|
179
175
|
console.print(
|
|
180
176
|
"[yellow]⚠ Classification parsing failed, defaulting to SIMPLE[/yellow]"
|
|
181
177
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
1
|
"""Configuration management for Kubrick CLI."""
|
|
3
2
|
|
|
4
3
|
import json
|
|
@@ -23,10 +22,8 @@ class KubrickConfig:
|
|
|
23
22
|
self.config_file = self.kubrick_dir / "config.json"
|
|
24
23
|
self.conversations_dir = self.kubrick_dir / "conversations"
|
|
25
24
|
|
|
26
|
-
# Ensure directories exist
|
|
27
25
|
self._ensure_directories()
|
|
28
26
|
|
|
29
|
-
# Load or create config (with optional setup wizard)
|
|
30
27
|
self.config = self._load_config(skip_wizard=skip_wizard)
|
|
31
28
|
|
|
32
29
|
def _ensure_directories(self):
|
|
@@ -49,29 +46,24 @@ class KubrickConfig:
|
|
|
49
46
|
with open(self.config_file, "r") as f:
|
|
50
47
|
loaded_config = json.load(f)
|
|
51
48
|
|
|
52
|
-
# Merge with defaults to ensure all keys exist
|
|
53
49
|
default_config = self._get_default_config()
|
|
54
50
|
default_config.update(loaded_config)
|
|
55
51
|
return default_config
|
|
56
52
|
|
|
57
53
|
except (json.JSONDecodeError, IOError):
|
|
58
|
-
# If config is corrupted, start fresh
|
|
59
54
|
return self._get_default_config()
|
|
60
55
|
else:
|
|
61
|
-
# First time setup - run wizard unless skipped
|
|
62
56
|
if not skip_wizard:
|
|
63
57
|
from .setup_wizard import SetupWizard
|
|
64
58
|
|
|
65
59
|
wizard_config = SetupWizard.run()
|
|
66
60
|
|
|
67
|
-
# Merge wizard config with defaults
|
|
68
61
|
config = self._get_default_config()
|
|
69
62
|
config.update(wizard_config)
|
|
70
63
|
|
|
71
64
|
self._save_config(config)
|
|
72
65
|
return config
|
|
73
66
|
else:
|
|
74
|
-
# Create default config without wizard
|
|
75
67
|
config = self._get_default_config()
|
|
76
68
|
self._save_config(config)
|
|
77
69
|
return config
|
|
@@ -105,7 +97,7 @@ class KubrickConfig:
|
|
|
105
97
|
"tool_timeout_seconds": 30,
|
|
106
98
|
"max_file_size_mb": 10,
|
|
107
99
|
# Display settings
|
|
108
|
-
"display_mode": "natural",
|
|
100
|
+
"display_mode": "natural",
|
|
109
101
|
"show_tool_results": True,
|
|
110
102
|
"show_progress": True,
|
|
111
103
|
# Task classification settings
|
|
@@ -156,20 +148,30 @@ class KubrickConfig:
|
|
|
156
148
|
with open(conversation_file, "w") as f:
|
|
157
149
|
json.dump(data, f, indent=2)
|
|
158
150
|
|
|
159
|
-
# Clean up old conversations if we exceed max
|
|
160
151
|
self._cleanup_old_conversations()
|
|
161
152
|
|
|
162
153
|
def load_conversation(self, conversation_id: str) -> Optional[Dict]:
|
|
163
154
|
"""
|
|
164
155
|
Load a conversation from disk.
|
|
165
156
|
|
|
157
|
+
Supports two modes:
|
|
158
|
+
1. By ID: Loads from ~/.kubrick/conversations/<id>.json
|
|
159
|
+
2. By path: Loads from absolute or relative file path
|
|
160
|
+
|
|
166
161
|
Args:
|
|
167
|
-
conversation_id:
|
|
162
|
+
conversation_id: Conversation ID or file path
|
|
168
163
|
|
|
169
164
|
Returns:
|
|
170
165
|
Dictionary with 'id', 'messages', and 'metadata', or None if not found
|
|
171
166
|
"""
|
|
172
|
-
|
|
167
|
+
if (
|
|
168
|
+
"/" in conversation_id
|
|
169
|
+
or "\\" in conversation_id
|
|
170
|
+
or conversation_id.endswith(".json")
|
|
171
|
+
):
|
|
172
|
+
conversation_file = Path(conversation_id).expanduser().resolve()
|
|
173
|
+
else:
|
|
174
|
+
conversation_file = self.conversations_dir / f"{conversation_id}.json"
|
|
173
175
|
|
|
174
176
|
if not conversation_file.exists():
|
|
175
177
|
return None
|
|
@@ -208,7 +210,6 @@ class KubrickConfig:
|
|
|
208
210
|
except (json.JSONDecodeError, IOError):
|
|
209
211
|
continue
|
|
210
212
|
|
|
211
|
-
# Sort by modification time (newest first)
|
|
212
213
|
conversations.sort(key=lambda x: x["modified"], reverse=True)
|
|
213
214
|
|
|
214
215
|
if limit:
|
|
@@ -240,9 +241,7 @@ class KubrickConfig:
|
|
|
240
241
|
conversations = list(self.conversations_dir.glob("*.json"))
|
|
241
242
|
|
|
242
243
|
if len(conversations) > max_conversations:
|
|
243
|
-
# Sort by modification time (oldest first)
|
|
244
244
|
conversations.sort(key=lambda x: x.stat().st_mtime)
|
|
245
245
|
|
|
246
|
-
# Delete oldest conversations
|
|
247
246
|
for conv_file in conversations[: len(conversations) - max_conversations]:
|
|
248
247
|
conv_file.unlink()
|
|
@@ -65,16 +65,12 @@ class DisplayManager:
|
|
|
65
65
|
elif self.display_mode in ("json", "verbose"):
|
|
66
66
|
self._display_json_result(tool_name, result, success)
|
|
67
67
|
|
|
68
|
-
def _display_natural_tool_call(
|
|
69
|
-
self, tool_name: str, parameters: Dict[str, Any]
|
|
70
|
-
):
|
|
68
|
+
def _display_natural_tool_call(self, tool_name: str, parameters: Dict[str, Any]):
|
|
71
69
|
"""Display tool call in natural language."""
|
|
72
70
|
description = self._get_natural_description(tool_name, parameters)
|
|
73
71
|
console.print(f"[cyan]→ {description}[/cyan]")
|
|
74
72
|
|
|
75
|
-
def _display_json_tool_call(
|
|
76
|
-
self, tool_name: str, parameters: Dict[str, Any]
|
|
77
|
-
):
|
|
73
|
+
def _display_json_tool_call(self, tool_name: str, parameters: Dict[str, Any]):
|
|
78
74
|
"""Display tool call as JSON panel."""
|
|
79
75
|
tool_data = {"tool": tool_name, "parameters": parameters}
|
|
80
76
|
json_str = json.dumps(tool_data, indent=2)
|
|
@@ -86,15 +82,12 @@ class DisplayManager:
|
|
|
86
82
|
):
|
|
87
83
|
"""Display result in natural language."""
|
|
88
84
|
if success:
|
|
89
|
-
# Extract key information from result
|
|
90
85
|
result_text = result.get("result", "")
|
|
91
86
|
|
|
92
|
-
# Truncate long results for display
|
|
93
87
|
if len(result_text) > 200:
|
|
94
88
|
preview = result_text[:200] + "..."
|
|
95
|
-
console.print(
|
|
96
|
-
|
|
97
|
-
)
|
|
89
|
+
console.print(f"[green]✓ {tool_name} succeeded[/green]")
|
|
90
|
+
console.print(f"[dim]{preview}[/dim]")
|
|
98
91
|
else:
|
|
99
92
|
console.print(f"[green]✓ {tool_name} succeeded[/green]")
|
|
100
93
|
else:
|
|
@@ -131,9 +124,7 @@ class DisplayManager:
|
|
|
131
124
|
elif tool_name == "write_file":
|
|
132
125
|
file_path = parameters.get("file_path", "unknown")
|
|
133
126
|
content_length = len(parameters.get("content", ""))
|
|
134
|
-
return
|
|
135
|
-
f"Writing {content_length} characters to {file_path}"
|
|
136
|
-
)
|
|
127
|
+
return f"Writing {content_length} characters to {file_path}"
|
|
137
128
|
|
|
138
129
|
elif tool_name == "edit_file":
|
|
139
130
|
file_path = parameters.get("file_path", "unknown")
|
|
@@ -151,7 +142,6 @@ class DisplayManager:
|
|
|
151
142
|
|
|
152
143
|
elif tool_name == "run_bash":
|
|
153
144
|
command = parameters.get("command", "")
|
|
154
|
-
# Truncate long commands
|
|
155
145
|
if len(command) > 60:
|
|
156
146
|
command = command[:60] + "..."
|
|
157
147
|
return f"Running bash command: {command}"
|
|
@@ -161,5 +151,4 @@ class DisplayManager:
|
|
|
161
151
|
return f"Creating directory {path}"
|
|
162
152
|
|
|
163
153
|
else:
|
|
164
|
-
# Fallback for unknown tools
|
|
165
154
|
return f"Calling {tool_name}"
|
|
@@ -30,7 +30,6 @@ class ExecutionStrategy:
|
|
|
30
30
|
- Quality (best models for complex tasks)
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
# Model tiers by provider
|
|
34
33
|
MODEL_TIERS = {
|
|
35
34
|
"openai": {
|
|
36
35
|
"fast": "gpt-3.5-turbo",
|
|
@@ -38,13 +37,12 @@ class ExecutionStrategy:
|
|
|
38
37
|
"best": "gpt-4",
|
|
39
38
|
},
|
|
40
39
|
"anthropic": {
|
|
41
|
-
"fast": "claude-haiku-4-5-20251001",
|
|
42
|
-
"balanced": "claude-sonnet-4-5-20250929",
|
|
43
|
-
"best": "claude-opus-4-1-20250805",
|
|
40
|
+
"fast": "claude-haiku-4-5-20251001",
|
|
41
|
+
"balanced": "claude-sonnet-4-5-20250929",
|
|
42
|
+
"best": "claude-opus-4-1-20250805",
|
|
44
43
|
},
|
|
45
44
|
"triton": {
|
|
46
|
-
|
|
47
|
-
"fast": None, # Use configured model
|
|
45
|
+
"fast": None,
|
|
48
46
|
"balanced": None,
|
|
49
47
|
"best": None,
|
|
50
48
|
},
|