tunacode-cli 0.0.22__tar.gz → 0.0.24__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.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- tunacode_cli-0.0.24/CLAUDE.md +120 -0
- tunacode_cli-0.0.24/MANIFEST.in +19 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/PKG-INFO +20 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/README.md +18 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/pyproject.toml +2 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/setup.py +1 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/commands.py +1 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/main.py +5 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/repl.py +23 -8
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/textual_app.py +19 -25
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/textual_bridge.py +2 -3
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/constants.py +1 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/context.py +0 -2
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/agents/main.py +21 -18
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/llm/planner.py +0 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/config_setup.py +6 -2
- tunacode_cli-0.0.24/src/tunacode/prompts/system.md +92 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/__init__.py +1 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/grep.py +2 -2
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/types.py +4 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/completers.py +5 -4
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/input.py +1 -2
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/lexers.py +0 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/output.py +2 -2
- tunacode_cli-0.0.24/src/tunacode/utils/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/PKG-INFO +20 -1
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/SOURCES.txt +6 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/requires.txt +1 -0
- tunacode_cli-0.0.24/tests/test_agent_initialization.py +147 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_architect_integration.py +22 -46
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_architect_simple.py +5 -37
- tunacode_cli-0.0.24/tests/test_config_setup_async.py +85 -0
- tunacode_cli-0.0.24/tests/test_fast_glob_search.py +111 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_orchestrator_planning_visibility.py +23 -15
- tunacode_cli-0.0.24/tests/test_react_thoughts.py +92 -0
- tunacode_cli-0.0.22/tests/test_fast_glob_search.py +0 -191
- tunacode_cli-0.0.22/tests/test_react_thoughts.py +0 -149
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/LICENSE +0 -0
- /tunacode_cli-0.0.22/src/tunacode/__init__.py → /tunacode_cli-0.0.24/TUNACODE.md +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/setup.cfg +0 -0
- {tunacode_cli-0.0.22/src/tunacode/core → tunacode_cli-0.0.24/src/tunacode}/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/cli/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/configuration/defaults.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/configuration/models.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.0.22/src/tunacode/core/background → tunacode_cli-0.0.24/src/tunacode/core}/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/agents/orchestrator.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/agents/planner_schema.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/agents/readonly.py +0 -0
- {tunacode_cli-0.0.22/src/tunacode/core/llm → tunacode_cli-0.0.24/src/tunacode/core/background}/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/background/manager.py +0 -0
- {tunacode_cli-0.0.22/src/tunacode/utils → tunacode_cli-0.0.24/src/tunacode/core/llm}/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/agent_setup.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/base.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/coordinator.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/environment_setup.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/setup/git_safety_setup.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/state.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/core/tool_handler.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/exceptions.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/prompts/system.txt +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/services/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/services/mcp.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/setup.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/base.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/bash.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/read_file.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/run_command.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/update_file.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/tools/write_file.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/console.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/constants.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/decorators.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/keybindings.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/panels.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/prompt_manager.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/tool_ui.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/ui/validators.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/bm25.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/diff_utils.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/file_utils.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/import_cache.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/ripgrep.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/system.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/text_utils.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode/utils/user_configuration.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/top_level.txt +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_background_manager.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_file_reference_expansion.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_json_tool_parsing.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_orchestrator_file_references.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_orchestrator_import.py +0 -0
- {tunacode_cli-0.0.22 → tunacode_cli-0.0.24}/tests/test_update_command.py +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
### Development Commands
|
|
8
|
+
```bash
|
|
9
|
+
# Install development environment
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
|
|
12
|
+
# Run linting (black, isort, flake8)
|
|
13
|
+
make lint
|
|
14
|
+
|
|
15
|
+
# Run tests
|
|
16
|
+
make test
|
|
17
|
+
pytest tests/ # Run all tests
|
|
18
|
+
pytest tests/test_import.py # Run single test file
|
|
19
|
+
pytest -k "test_name" # Run specific test
|
|
20
|
+
|
|
21
|
+
# Run tests with coverage
|
|
22
|
+
make coverage
|
|
23
|
+
|
|
24
|
+
# Build distribution packages
|
|
25
|
+
make build
|
|
26
|
+
|
|
27
|
+
# Clean build artifacts
|
|
28
|
+
make clean
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Version Management
|
|
32
|
+
When updating versions, modify both:
|
|
33
|
+
- `pyproject.toml`: version field
|
|
34
|
+
- `src/tunacode/constants.py`: VERSION constant
|
|
35
|
+
|
|
36
|
+
## Architecture
|
|
37
|
+
|
|
38
|
+
TunaCode is a CLI tool that provides an AI-powered coding assistant using pydantic-ai. Key architectural decisions:
|
|
39
|
+
|
|
40
|
+
### Agent System
|
|
41
|
+
- Uses `pydantic-ai` for LLM agent implementation
|
|
42
|
+
- Central agent in `src/tunacode/core/agents/main.py` with retryable tools
|
|
43
|
+
- Supports multiple LLM providers (Anthropic, OpenAI, Google, OpenRouter) through unified interface
|
|
44
|
+
- Model format: `provider:model-name` (e.g., `openai:gpt-4`, `anthropic:claude-3-opus`)
|
|
45
|
+
|
|
46
|
+
### Tool System
|
|
47
|
+
Four internal tools with confirmation UI:
|
|
48
|
+
1. `read_file` - Read file contents
|
|
49
|
+
2. `write_file` - Create new files (fails if exists)
|
|
50
|
+
3. `update_file` - Update existing files with target/patch pattern
|
|
51
|
+
4. `run_command` - Execute shell commands
|
|
52
|
+
|
|
53
|
+
Tools extend `BaseTool` or `FileBasedTool` base classes. External tools supported via MCP (Model Context Protocol).
|
|
54
|
+
|
|
55
|
+
### State Management
|
|
56
|
+
- `StateManager` (core/state.py) maintains all session state
|
|
57
|
+
- Includes user config, agent instances, message history, costs, permissions
|
|
58
|
+
- Single source of truth passed throughout the application
|
|
59
|
+
|
|
60
|
+
### Command System
|
|
61
|
+
- Command registry pattern in `cli/commands.py`
|
|
62
|
+
- Commands implement `BaseCommand` with `matches()` and `execute()` methods
|
|
63
|
+
- Registered via `@CommandRegistry.register` decorator
|
|
64
|
+
- Process flow: REPL → CommandRegistry → Command → Action
|
|
65
|
+
|
|
66
|
+
### Setup Coordinator
|
|
67
|
+
Modular setup with validation steps:
|
|
68
|
+
1. Environment detection
|
|
69
|
+
2. Model validation
|
|
70
|
+
3. Configuration setup
|
|
71
|
+
4. Git safety checks
|
|
72
|
+
Each step implements `BaseSetupStep` interface.
|
|
73
|
+
|
|
74
|
+
### UI Components
|
|
75
|
+
- REPL uses `prompt_toolkit` for multiline input with syntax highlighting
|
|
76
|
+
- Output formatting via `rich` library
|
|
77
|
+
- Tool confirmations show diffs for file operations
|
|
78
|
+
- Spinner during agent processing
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
### User Configuration
|
|
83
|
+
Location: `~/.config/tunacode.json`
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"default_model": "provider:model-name",
|
|
87
|
+
"env": {
|
|
88
|
+
"ANTHROPIC_API_KEY": "...",
|
|
89
|
+
"OPENAI_API_KEY": "..."
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Project Guide
|
|
95
|
+
Location: `TUNACODE.md` in project root
|
|
96
|
+
- Project-specific context for the AI assistant
|
|
97
|
+
- Loaded automatically when present
|
|
98
|
+
- Can include codebase conventions, architecture notes
|
|
99
|
+
|
|
100
|
+
## Key Design Patterns
|
|
101
|
+
|
|
102
|
+
### Error Handling
|
|
103
|
+
- Custom exceptions in `exceptions.py`
|
|
104
|
+
- `ModelRetry` from pydantic-ai for retryable errors
|
|
105
|
+
- Graceful degradation for missing features
|
|
106
|
+
|
|
107
|
+
### Permissions
|
|
108
|
+
- File operation permissions tracked per session
|
|
109
|
+
- "Yolo mode" to skip confirmations: `/yolo`
|
|
110
|
+
- Permissions stored in StateManager
|
|
111
|
+
|
|
112
|
+
### Async Architecture
|
|
113
|
+
- All agent operations are async
|
|
114
|
+
- Tool executions use async/await
|
|
115
|
+
- REPL handles async with prompt_toolkit integration
|
|
116
|
+
|
|
117
|
+
### Safety Notes
|
|
118
|
+
- No automatic git commits (removed for safety)
|
|
119
|
+
- File operations require explicit confirmation
|
|
120
|
+
- Encourages git branches for experiments: `/branch <name>`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Include all prompt files
|
|
2
|
+
include src/tunacode/prompts/*.txt
|
|
3
|
+
include src/tunacode/prompts/*.md
|
|
4
|
+
|
|
5
|
+
# Include other important files
|
|
6
|
+
include README.md
|
|
7
|
+
include LICENSE
|
|
8
|
+
include pyproject.toml
|
|
9
|
+
include CLAUDE.md
|
|
10
|
+
include TUNACODE.md
|
|
11
|
+
|
|
12
|
+
# Include test files (optional, but good for development)
|
|
13
|
+
recursive-include tests *.py
|
|
14
|
+
|
|
15
|
+
# Exclude compiled files
|
|
16
|
+
global-exclude *.pyc
|
|
17
|
+
global-exclude __pycache__
|
|
18
|
+
global-exclude *.so
|
|
19
|
+
global-exclude *.egg-info
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunacode-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.24
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,6 +30,7 @@ Requires-Dist: black; extra == "dev"
|
|
|
30
30
|
Requires-Dist: flake8; extra == "dev"
|
|
31
31
|
Requires-Dist: isort; extra == "dev"
|
|
32
32
|
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
33
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
35
|
Requires-Dist: textual-dev; extra == "dev"
|
|
35
36
|
Dynamic: license-file
|
|
@@ -131,6 +132,24 @@ Dynamic: license-file
|
|
|
131
132
|
pip install tunacode-cli
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
#### Development Installation
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Clone the repository
|
|
139
|
+
git clone https://github.com/larock22/tunacode.git
|
|
140
|
+
cd tunacode
|
|
141
|
+
|
|
142
|
+
# Run the setup script
|
|
143
|
+
./scripts/setup_dev_env.sh
|
|
144
|
+
|
|
145
|
+
# Or manually:
|
|
146
|
+
python3 -m venv venv
|
|
147
|
+
source venv/bin/activate
|
|
148
|
+
pip install -e ".[dev]"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
|
152
|
+
|
|
134
153
|
#### One-line Install (Linux/macOS)
|
|
135
154
|
|
|
136
155
|
```bash
|
|
@@ -95,6 +95,24 @@
|
|
|
95
95
|
pip install tunacode-cli
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
#### Development Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Clone the repository
|
|
102
|
+
git clone https://github.com/larock22/tunacode.git
|
|
103
|
+
cd tunacode
|
|
104
|
+
|
|
105
|
+
# Run the setup script
|
|
106
|
+
./scripts/setup_dev_env.sh
|
|
107
|
+
|
|
108
|
+
# Or manually:
|
|
109
|
+
python3 -m venv venv
|
|
110
|
+
source venv/bin/activate
|
|
111
|
+
pip install -e ".[dev]"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
|
115
|
+
|
|
98
116
|
#### One-line Install (Linux/macOS)
|
|
99
117
|
|
|
100
118
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tunacode-cli"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.24"
|
|
8
8
|
description = "Your agentic CLI developer."
|
|
9
9
|
keywords = ["cli", "agent", "development", "automation"]
|
|
10
10
|
readme = "README.md"
|
|
@@ -42,6 +42,7 @@ dev = [
|
|
|
42
42
|
"flake8",
|
|
43
43
|
"isort",
|
|
44
44
|
"pytest",
|
|
45
|
+
"pytest-asyncio",
|
|
45
46
|
"pytest-cov",
|
|
46
47
|
"textual-dev",
|
|
47
48
|
]
|
|
@@ -192,7 +192,7 @@ class ArchitectCommand(SimpleCommand):
|
|
|
192
192
|
await ui.error("Usage: /architect [on|off]")
|
|
193
193
|
return
|
|
194
194
|
else:
|
|
195
|
-
state.architect_mode = not getattr(state,
|
|
195
|
+
state.architect_mode = not getattr(state, "architect_mode", False)
|
|
196
196
|
status = "ON" if state.architect_mode else "OFF"
|
|
197
197
|
if state.architect_mode:
|
|
198
198
|
await ui.success(f"Architect mode {status} - Requests will be planned before execution")
|
|
@@ -14,6 +14,7 @@ from tunacode.core.state import StateManager
|
|
|
14
14
|
from tunacode.setup import setup
|
|
15
15
|
from tunacode.ui import console as ui
|
|
16
16
|
from tunacode.utils.system import check_for_updates
|
|
17
|
+
from tunacode.exceptions import UserAbortError
|
|
17
18
|
|
|
18
19
|
app_settings = ApplicationSettings()
|
|
19
20
|
app = typer.Typer(help="🐟 TunaCode - Your AI-powered development assistant")
|
|
@@ -49,8 +50,12 @@ def main(
|
|
|
49
50
|
try:
|
|
50
51
|
await setup(run_setup, state_manager, cli_config)
|
|
51
52
|
await repl(state_manager)
|
|
53
|
+
except (KeyboardInterrupt, UserAbortError):
|
|
54
|
+
update_task.cancel()
|
|
55
|
+
return
|
|
52
56
|
except Exception as e:
|
|
53
57
|
from tunacode.exceptions import ConfigurationError
|
|
58
|
+
|
|
54
59
|
if isinstance(e, ConfigurationError):
|
|
55
60
|
# ConfigurationError already printed helpful message, just exit cleanly
|
|
56
61
|
update_task.cancel() # Cancel the update check
|
|
@@ -172,7 +172,7 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
172
172
|
return _tool_handler(part, node, state_manager)
|
|
173
173
|
|
|
174
174
|
# Check if architect mode is enabled
|
|
175
|
-
if getattr(state_manager.session,
|
|
175
|
+
if getattr(state_manager.session, "architect_mode", False):
|
|
176
176
|
# Expand @file references before sending to the orchestrator
|
|
177
177
|
try:
|
|
178
178
|
from tunacode.utils.text_utils import expand_file_refs
|
|
@@ -184,14 +184,18 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
184
184
|
# Use orchestrator for planning and execution
|
|
185
185
|
orchestrator = OrchestratorAgent(state_manager)
|
|
186
186
|
results = await orchestrator.run(text, state_manager.session.current_model)
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
if output:
|
|
189
189
|
# Process results from all sub-agents
|
|
190
190
|
for res in results:
|
|
191
191
|
# Check if result exists and has output
|
|
192
|
-
if
|
|
192
|
+
if (
|
|
193
|
+
hasattr(res, "result")
|
|
194
|
+
and res.result is not None
|
|
195
|
+
and hasattr(res.result, "output")
|
|
196
|
+
):
|
|
193
197
|
await ui.agent(res.result.output)
|
|
194
|
-
|
|
198
|
+
|
|
195
199
|
if not results:
|
|
196
200
|
# Fallback: show that the request was processed
|
|
197
201
|
await ui.muted("Request completed")
|
|
@@ -204,7 +208,7 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
204
208
|
except ValueError as e:
|
|
205
209
|
await ui.error(str(e))
|
|
206
210
|
return
|
|
207
|
-
|
|
211
|
+
|
|
208
212
|
# Use normal agent processing
|
|
209
213
|
res = await agent.process_request(
|
|
210
214
|
state_manager.session.current_model,
|
|
@@ -219,7 +223,11 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
219
223
|
if isinstance(msg, dict) and "thought" in msg:
|
|
220
224
|
await ui.muted(f"THOUGHT: {msg['thought']}")
|
|
221
225
|
# Check if result exists and has output
|
|
222
|
-
if
|
|
226
|
+
if (
|
|
227
|
+
hasattr(res, "result")
|
|
228
|
+
and res.result is not None
|
|
229
|
+
and hasattr(res.result, "output")
|
|
230
|
+
):
|
|
223
231
|
await ui.agent(res.result.output)
|
|
224
232
|
else:
|
|
225
233
|
# Fallback: show that the request was processed
|
|
@@ -274,6 +282,7 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
274
282
|
|
|
275
283
|
async def repl(state_manager: StateManager):
|
|
276
284
|
action = None
|
|
285
|
+
ctrl_c_pressed = False
|
|
277
286
|
|
|
278
287
|
# Professional startup information
|
|
279
288
|
await ui.muted(f"• Model: {state_manager.session.current_model}")
|
|
@@ -286,12 +295,18 @@ async def repl(state_manager: StateManager):
|
|
|
286
295
|
while True:
|
|
287
296
|
try:
|
|
288
297
|
line = await ui.multiline_input(state_manager, _command_registry)
|
|
289
|
-
except
|
|
290
|
-
|
|
298
|
+
except UserAbortError:
|
|
299
|
+
if ctrl_c_pressed:
|
|
300
|
+
break
|
|
301
|
+
ctrl_c_pressed = True
|
|
302
|
+
await ui.warning("Hit Ctrl+C again to exit")
|
|
303
|
+
continue
|
|
291
304
|
|
|
292
305
|
if not line:
|
|
293
306
|
continue
|
|
294
307
|
|
|
308
|
+
ctrl_c_pressed = False
|
|
309
|
+
|
|
295
310
|
if line.lower() in ["exit", "quit"]:
|
|
296
311
|
break
|
|
297
312
|
|
|
@@ -11,18 +11,13 @@ Provides a rich, interactive terminal user interface with:
|
|
|
11
11
|
import asyncio
|
|
12
12
|
from typing import Optional
|
|
13
13
|
|
|
14
|
-
from
|
|
15
|
-
from textual import on, work
|
|
14
|
+
from textual import on
|
|
16
15
|
from textual.app import App, ComposeResult
|
|
17
16
|
from textual.binding import Binding
|
|
18
17
|
from textual.containers import Container, Horizontal, Vertical, VerticalScroll
|
|
19
18
|
from textual.message import Message
|
|
20
|
-
from textual.
|
|
21
|
-
from textual.widgets import Button, Footer, Header, Input, Label
|
|
22
|
-
from textual.widgets import Markdown as MarkdownWidget
|
|
23
|
-
from textual.widgets import Static, TabbedContent, TabPane, TextArea
|
|
19
|
+
from textual.widgets import Button, Footer, Header, Static, TextArea
|
|
24
20
|
|
|
25
|
-
from tunacode.configuration.settings import ApplicationSettings
|
|
26
21
|
from tunacode.core.state import StateManager
|
|
27
22
|
from tunacode.setup import setup
|
|
28
23
|
from tunacode.utils.system import check_for_updates
|
|
@@ -130,78 +125,78 @@ class TunaCodeApp(App):
|
|
|
130
125
|
border: thick $primary;
|
|
131
126
|
padding: 1;
|
|
132
127
|
}
|
|
133
|
-
|
|
128
|
+
|
|
134
129
|
.sidebar-title {
|
|
135
130
|
text-align: center;
|
|
136
131
|
color: $primary;
|
|
137
132
|
margin-bottom: 1;
|
|
138
133
|
}
|
|
139
|
-
|
|
134
|
+
|
|
140
135
|
.section-title {
|
|
141
136
|
color: $accent;
|
|
142
137
|
margin: 1 0;
|
|
143
138
|
}
|
|
144
|
-
|
|
139
|
+
|
|
145
140
|
.command-item {
|
|
146
141
|
color: $text-muted;
|
|
147
142
|
margin-left: 1;
|
|
148
143
|
}
|
|
149
|
-
|
|
144
|
+
|
|
150
145
|
.status-ready {
|
|
151
146
|
color: $success;
|
|
152
147
|
}
|
|
153
|
-
|
|
148
|
+
|
|
154
149
|
.status-busy {
|
|
155
150
|
color: $warning;
|
|
156
151
|
}
|
|
157
|
-
|
|
152
|
+
|
|
158
153
|
.status-error {
|
|
159
154
|
color: $error;
|
|
160
155
|
}
|
|
161
|
-
|
|
156
|
+
|
|
162
157
|
ChatHistory {
|
|
163
158
|
border: thick $primary;
|
|
164
159
|
padding: 1;
|
|
165
160
|
height: 1fr;
|
|
166
161
|
}
|
|
167
|
-
|
|
162
|
+
|
|
168
163
|
.user-message {
|
|
169
164
|
background: $surface;
|
|
170
165
|
border-left: thick $primary;
|
|
171
166
|
padding: 1;
|
|
172
167
|
margin: 1 0;
|
|
173
168
|
}
|
|
174
|
-
|
|
169
|
+
|
|
175
170
|
.agent-message {
|
|
176
171
|
background: $surface;
|
|
177
172
|
border-left: thick $success;
|
|
178
173
|
padding: 1;
|
|
179
174
|
margin: 1 0;
|
|
180
175
|
}
|
|
181
|
-
|
|
176
|
+
|
|
182
177
|
.system-message {
|
|
183
178
|
background: $surface;
|
|
184
179
|
border-left: thick $warning;
|
|
185
180
|
padding: 1;
|
|
186
181
|
margin: 1 0;
|
|
187
182
|
}
|
|
188
|
-
|
|
183
|
+
|
|
189
184
|
.tool-message {
|
|
190
185
|
background: $surface;
|
|
191
186
|
border-left: thick $accent;
|
|
192
187
|
padding: 1;
|
|
193
188
|
margin: 1 0;
|
|
194
189
|
}
|
|
195
|
-
|
|
190
|
+
|
|
196
191
|
InputArea {
|
|
197
192
|
height: 5;
|
|
198
193
|
padding: 1;
|
|
199
194
|
}
|
|
200
|
-
|
|
195
|
+
|
|
201
196
|
#message-input {
|
|
202
197
|
height: 3;
|
|
203
198
|
}
|
|
204
|
-
|
|
199
|
+
|
|
205
200
|
#send-button {
|
|
206
201
|
width: 10;
|
|
207
202
|
margin-left: 1;
|
|
@@ -250,7 +245,8 @@ class TunaCodeApp(App):
|
|
|
250
245
|
)
|
|
251
246
|
self.chat_history.add_message(
|
|
252
247
|
"System",
|
|
253
|
-
"⚠️ IMPORTANT: Always use git branches before making major changes\
|
|
248
|
+
"⚠️ IMPORTANT: Always use git branches before making major changes\n"
|
|
249
|
+
"Type '/help' for available commands",
|
|
254
250
|
"system",
|
|
255
251
|
)
|
|
256
252
|
|
|
@@ -280,7 +276,7 @@ class TunaCodeApp(App):
|
|
|
280
276
|
"""Handle slash commands."""
|
|
281
277
|
if command == "/help":
|
|
282
278
|
help_text = """Available Commands:
|
|
283
|
-
|
|
279
|
+
|
|
284
280
|
/help - Show this help message
|
|
285
281
|
/clear - Clear chat history
|
|
286
282
|
/model - Show current model info
|
|
@@ -371,8 +367,6 @@ def main():
|
|
|
371
367
|
|
|
372
368
|
# Initialize state manager
|
|
373
369
|
state_manager = StateManager()
|
|
374
|
-
app_settings = ApplicationSettings()
|
|
375
|
-
|
|
376
370
|
# Show banner
|
|
377
371
|
print("🐟 TunaCode - Modern AI Development Assistant")
|
|
378
372
|
print("=" * 50)
|
|
@@ -5,9 +5,8 @@ This module adapts the existing REPL and agent processing logic to work
|
|
|
5
5
|
with the new Textual-based interface while maintaining compatibility.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import asyncio
|
|
9
8
|
from asyncio.exceptions import CancelledError
|
|
10
|
-
from typing import Callable
|
|
9
|
+
from typing import Callable
|
|
11
10
|
|
|
12
11
|
from pydantic_ai.exceptions import UnexpectedModelBehavior
|
|
13
12
|
|
|
@@ -129,7 +128,7 @@ class TextualAgentBridge:
|
|
|
129
128
|
# Check if confirmation is needed
|
|
130
129
|
if tool_handler.should_confirm(part.tool_name):
|
|
131
130
|
# Create confirmation request
|
|
132
|
-
|
|
131
|
+
tool_handler.create_confirmation_request(part.tool_name, args)
|
|
133
132
|
|
|
134
133
|
# For now, show a simple confirmation in the UI
|
|
135
134
|
# In a full implementation, this would show a proper modal dialog
|
|
@@ -7,11 +7,22 @@ Handles agent creation, configuration, and request processing.
|
|
|
7
7
|
import json
|
|
8
8
|
import re
|
|
9
9
|
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
10
11
|
from typing import Optional
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
from tunacode.core.state import StateManager
|
|
14
|
+
from tunacode.services.mcp import get_mcp_servers
|
|
15
|
+
from tunacode.tools.bash import bash
|
|
16
|
+
from tunacode.tools.grep import grep
|
|
17
|
+
from tunacode.tools.read_file import read_file
|
|
18
|
+
from tunacode.tools.run_command import run_command
|
|
19
|
+
from tunacode.tools.update_file import update_file
|
|
20
|
+
from tunacode.tools.write_file import write_file
|
|
21
|
+
from tunacode.types import (AgentRun, ErrorMessage, ModelName, PydanticAgent, ToolCallback,
|
|
22
|
+
ToolCallId, ToolName)
|
|
13
23
|
|
|
14
24
|
|
|
25
|
+
# Lazy import for Agent and Tool
|
|
15
26
|
def get_agent_tool():
|
|
16
27
|
import importlib
|
|
17
28
|
|
|
@@ -26,18 +37,6 @@ def get_model_messages():
|
|
|
26
37
|
return messages.ModelRequest, messages.ToolReturnPart
|
|
27
38
|
|
|
28
39
|
|
|
29
|
-
from tunacode.core.state import StateManager
|
|
30
|
-
from tunacode.services.mcp import get_mcp_servers
|
|
31
|
-
from tunacode.tools.bash import bash
|
|
32
|
-
from tunacode.tools.grep import grep
|
|
33
|
-
from tunacode.tools.read_file import read_file
|
|
34
|
-
from tunacode.tools.run_command import run_command
|
|
35
|
-
from tunacode.tools.update_file import update_file
|
|
36
|
-
from tunacode.tools.write_file import write_file
|
|
37
|
-
from tunacode.types import (AgentRun, ErrorMessage, ModelName, PydanticAgent, ToolCallback,
|
|
38
|
-
ToolCallId, ToolName)
|
|
39
|
-
|
|
40
|
-
|
|
41
40
|
async def _process_node(node, tool_callback: Optional[ToolCallback], state_manager: StateManager):
|
|
42
41
|
if hasattr(node, "request"):
|
|
43
42
|
state_manager.session.messages.append(node.request)
|
|
@@ -125,21 +124,25 @@ async def _process_node(node, tool_callback: Optional[ToolCallback], state_manag
|
|
|
125
124
|
|
|
126
125
|
def get_or_create_agent(model: ModelName, state_manager: StateManager) -> PydanticAgent:
|
|
127
126
|
if model not in state_manager.session.agents:
|
|
128
|
-
max_retries = state_manager.session.user_config
|
|
127
|
+
max_retries = state_manager.session.user_config.get("settings", {}).get("max_retries", 3)
|
|
129
128
|
|
|
130
129
|
# Lazy import Agent and Tool
|
|
131
130
|
Agent, Tool = get_agent_tool()
|
|
132
131
|
|
|
133
132
|
# Load system prompt
|
|
134
|
-
import os
|
|
135
|
-
from pathlib import Path
|
|
136
|
-
|
|
137
133
|
prompt_path = Path(__file__).parent.parent.parent / "prompts" / "system.md"
|
|
138
134
|
try:
|
|
139
135
|
with open(prompt_path, "r", encoding="utf-8") as f:
|
|
140
136
|
system_prompt = f.read().strip()
|
|
141
137
|
except FileNotFoundError:
|
|
142
|
-
|
|
138
|
+
# Fallback to system.txt if system.md not found
|
|
139
|
+
prompt_path = Path(__file__).parent.parent.parent / "prompts" / "system.txt"
|
|
140
|
+
try:
|
|
141
|
+
with open(prompt_path, "r", encoding="utf-8") as f:
|
|
142
|
+
system_prompt = f.read().strip()
|
|
143
|
+
except FileNotFoundError:
|
|
144
|
+
# Use a default system prompt if neither file exists
|
|
145
|
+
system_prompt = "You are a helpful AI assistant for software development tasks."
|
|
143
146
|
|
|
144
147
|
state_manager.session.agents[model] = Agent(
|
|
145
148
|
model=model,
|
|
@@ -99,11 +99,15 @@ class ConfigSetup(BaseSetup):
|
|
|
99
99
|
" [green]tunacode --model 'anthropic:claude-3-opus' --key 'your-key'[/green]"
|
|
100
100
|
)
|
|
101
101
|
console.print(
|
|
102
|
-
" [green]tunacode --model 'openrouter:anthropic/claude-3.5-sonnet'
|
|
102
|
+
" [green]tunacode --model 'openrouter:anthropic/claude-3.5-sonnet' "
|
|
103
|
+
"--key 'your-key' --baseurl 'https://openrouter.ai/api/v1'[/green]"
|
|
103
104
|
)
|
|
104
105
|
console.print("\n[yellow]Run 'tunacode --help' for more options[/yellow]\n")
|
|
105
106
|
from tunacode.exceptions import ConfigurationError
|
|
106
|
-
|
|
107
|
+
|
|
108
|
+
raise ConfigurationError(
|
|
109
|
+
"No configuration found. Please use CLI flags to configure."
|
|
110
|
+
)
|
|
107
111
|
|
|
108
112
|
if not self.state_manager.session.user_config.get("default_model"):
|
|
109
113
|
raise ConfigurationError(
|