tunacode-cli 0.0.5__tar.gz → 0.0.6__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.6/PKG-INFO +235 -0
- tunacode_cli-0.0.6/README.md +199 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/pyproject.toml +3 -2
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/cli/commands.py +91 -33
- tunacode_cli-0.0.6/src/tunacode/cli/model_selector.py +178 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/cli/repl.py +11 -10
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/configuration/models.py +11 -1
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/constants.py +10 -10
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/context.py +1 -3
- tunacode_cli-0.0.6/src/tunacode/core/agents/main.py +77 -0
- tunacode_cli-0.0.6/src/tunacode/core/agents/tinyagent_main.py +171 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/git_safety_setup.py +39 -51
- tunacode_cli-0.0.6/src/tunacode/core/setup/optimized_coordinator.py +73 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/exceptions.py +0 -2
- tunacode_cli-0.0.6/src/tunacode/services/enhanced_undo_service.py +322 -0
- tunacode_cli-0.0.6/src/tunacode/services/project_undo_service.py +311 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/services/undo_service.py +13 -16
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/base.py +11 -20
- tunacode_cli-0.0.6/src/tunacode/tools/tinyagent_tools.py +103 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/update_file.py +24 -14
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/write_file.py +9 -7
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/completers.py +98 -33
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/input.py +8 -7
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/keybindings.py +1 -3
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/lexers.py +16 -17
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/output.py +9 -3
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/panels.py +4 -4
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/prompt_manager.py +6 -4
- tunacode_cli-0.0.6/src/tunacode/utils/lazy_imports.py +59 -0
- tunacode_cli-0.0.6/src/tunacode/utils/regex_cache.py +33 -0
- tunacode_cli-0.0.6/src/tunacode_cli.egg-info/PKG-INFO +235 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode_cli.egg-info/SOURCES.txt +8 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode_cli.egg-info/requires.txt +2 -1
- tunacode_cli-0.0.5/PKG-INFO +0 -247
- tunacode_cli-0.0.5/README.md +0 -212
- tunacode_cli-0.0.5/src/tunacode/core/agents/main.py +0 -119
- tunacode_cli-0.0.5/src/tunacode_cli.egg-info/PKG-INFO +0 -247
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/LICENSE +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/setup.cfg +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/cli/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/cli/main.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/configuration/defaults.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/agent_setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/base.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/config_setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/coordinator.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/environment_setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/setup/undo_setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/state.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/core/tool_handler.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/prompts/system.txt +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/services/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/services/mcp.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/setup.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/read_file.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/tools/run_command.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/types.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/console.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/constants.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/decorators.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/tool_ui.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/ui/validators.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/__init__.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/bm25.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/diff_utils.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/file_utils.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/ripgrep.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/system.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/text_utils.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode/utils/user_configuration.py +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
- {tunacode_cli-0.0.5 → tunacode_cli-0.0.6}/src/tunacode_cli.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tunacode-cli
|
|
3
|
+
Version: 0.0.6
|
|
4
|
+
Summary: Your agentic CLI developer.
|
|
5
|
+
Author-email: larock22 <noreply@github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/larock22/tunacode
|
|
8
|
+
Project-URL: Repository, https://github.com/larock22/tunacode
|
|
9
|
+
Keywords: cli,agent,development,automation
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development
|
|
18
|
+
Classifier: Topic :: Utilities
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: prompt_toolkit==3.0.51
|
|
23
|
+
Requires-Dist: tiny_agent_os>=0.1.0
|
|
24
|
+
Requires-Dist: pygments==2.19.1
|
|
25
|
+
Requires-Dist: rich==14.0.0
|
|
26
|
+
Requires-Dist: typer==0.15.3
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build; extra == "dev"
|
|
30
|
+
Requires-Dist: black; extra == "dev"
|
|
31
|
+
Requires-Dist: flake8; extra == "dev"
|
|
32
|
+
Requires-Dist: isort; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# 🐟 TunaCode
|
|
38
|
+
|
|
39
|
+
[](https://badge.fury.io/py/tunacode-cli)
|
|
40
|
+
[](https://www.python.org/downloads/)
|
|
41
|
+
[](https://opensource.org/licenses/MIT)
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
**Your agentic CLI developer** - An open-source alternative to Claude Code, Copilot, and Cursor with multi-provider LLM support.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## ✨ What's New (v0.1.0)
|
|
49
|
+
|
|
50
|
+
- 🚀 **60% faster startup** with lazy loading and optimizations
|
|
51
|
+
- 🤖 **TinyAgent integration** for robust ReAct-based interactions
|
|
52
|
+
- 🛡️ **Three-layer undo system** with automatic failover
|
|
53
|
+
- 📊 **Enhanced model selection** with fuzzy matching and cost indicators
|
|
54
|
+
- 📁 **Project-local backups** in `.tunacode/` directory
|
|
55
|
+
|
|
56
|
+
## 🎯 Features
|
|
57
|
+
|
|
58
|
+
### Core Capabilities
|
|
59
|
+
- **🔓 No vendor lock-in** - Use any LLM provider (OpenAI, Anthropic, Google, 100+ via OpenRouter)
|
|
60
|
+
- **⚡ Fast & responsive** - Optimized for speed with <5ms operation overhead
|
|
61
|
+
- **🛡️ Safe operations** - Three-layer undo system ensures nothing is lost
|
|
62
|
+
- **🎨 Modern CLI** - Beautiful terminal UI with syntax highlighting
|
|
63
|
+
- **💰 Cost tracking** - Monitor tokens and costs per session
|
|
64
|
+
|
|
65
|
+
### Developer Experience
|
|
66
|
+
- **🔄 Hot model switching** - Change models mid-conversation with `/model`
|
|
67
|
+
- **📝 Project guides** - Customize behavior with `TUNACODE.md` files
|
|
68
|
+
- **🚀 YOLO mode** - Skip confirmations when you're confident
|
|
69
|
+
- **🔧 MCP support** - Extend with Model Context Protocol servers
|
|
70
|
+
- **📊 Git integration** - Automatic branch creation and undo support
|
|
71
|
+
|
|
72
|
+
## 🚀 Quick Start
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Install from PyPI
|
|
76
|
+
pip install tunacode-cli
|
|
77
|
+
|
|
78
|
+
# Run setup (first time only)
|
|
79
|
+
tunacode
|
|
80
|
+
|
|
81
|
+
# Start coding!
|
|
82
|
+
tunacode
|
|
83
|
+
> Help me refactor this codebase to use async/await
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 📋 Commands
|
|
87
|
+
|
|
88
|
+
| Command | Description | Example |
|
|
89
|
+
|---------|-------------|---------|
|
|
90
|
+
| `/model` or `/m` | List and switch models | `/model 3` or `/m opus` |
|
|
91
|
+
| `/yolo` | Toggle confirmation skipping | `/yolo` |
|
|
92
|
+
| `/undo` | Undo last file operation | `/undo` |
|
|
93
|
+
| `/clear` | Clear conversation history | `/clear` |
|
|
94
|
+
| `/branch <name>` | Create new git branch | `/branch feature/auth` |
|
|
95
|
+
| `/compact` | Summarize and trim history | `/compact` |
|
|
96
|
+
| `/help` | Show all commands | `/help` |
|
|
97
|
+
|
|
98
|
+
## 🔧 Configuration
|
|
99
|
+
|
|
100
|
+
Configuration is stored in `~/.config/tunacode.json`:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"default_model": "openai:gpt-4o",
|
|
105
|
+
"env": {
|
|
106
|
+
"OPENAI_API_KEY": "sk-...",
|
|
107
|
+
"ANTHROPIC_API_KEY": "sk-ant-...",
|
|
108
|
+
"OPENROUTER_API_KEY": "sk-or-..."
|
|
109
|
+
},
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"github": {
|
|
112
|
+
"command": "npx",
|
|
113
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
114
|
+
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "..."}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Using OpenRouter (100+ Models)
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Add your OpenRouter API key to config
|
|
124
|
+
# Then run with OpenRouter base URL:
|
|
125
|
+
OPENAI_BASE_URL="https://openrouter.ai/api/v1" tunacode
|
|
126
|
+
|
|
127
|
+
# Use any OpenRouter model:
|
|
128
|
+
/model openrouter:anthropic/claude-3-opus
|
|
129
|
+
/model openrouter:mistralai/devstral-small
|
|
130
|
+
/model openrouter:openai/gpt-4.1
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 🛡️ Undo System
|
|
134
|
+
|
|
135
|
+
TunaCode provides **three layers of protection** for your files:
|
|
136
|
+
|
|
137
|
+
1. **Git commits** - Primary undo mechanism (if available)
|
|
138
|
+
2. **Operation log** - Tracks changes with content (<100KB files)
|
|
139
|
+
3. **File backups** - Physical copies in `.tunacode/backups/`
|
|
140
|
+
|
|
141
|
+
All undo data is stored locally in your project:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
your-project/
|
|
145
|
+
└── .tunacode/ # Auto-created, gitignored
|
|
146
|
+
├── backups/ # Timestamped file copies
|
|
147
|
+
├── operations.jsonl # Change history
|
|
148
|
+
└── README.md # Explains the directory
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 🎯 Project Customization
|
|
152
|
+
|
|
153
|
+
Create a `TUNACODE.md` file in your project root:
|
|
154
|
+
|
|
155
|
+
```markdown
|
|
156
|
+
# Project Guidelines for TunaCode
|
|
157
|
+
|
|
158
|
+
## Tech Stack
|
|
159
|
+
- Next.js 14 with App Router
|
|
160
|
+
- TypeScript with strict mode
|
|
161
|
+
- Tailwind CSS for styling
|
|
162
|
+
|
|
163
|
+
## Conventions
|
|
164
|
+
- Use arrow functions for components
|
|
165
|
+
- Prefer server components where possible
|
|
166
|
+
- Follow conventional commits
|
|
167
|
+
|
|
168
|
+
## Commands
|
|
169
|
+
- `npm run dev` - Start development
|
|
170
|
+
- `npm test` - Run tests
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## ⚡ Performance
|
|
174
|
+
|
|
175
|
+
TunaCode is optimized for speed:
|
|
176
|
+
- **Startup time**: ~0.5-0.8 seconds
|
|
177
|
+
- **Model switching**: ~100ms
|
|
178
|
+
- **File operations**: ~5ms overhead
|
|
179
|
+
- **API calls**: Connection pooling enabled
|
|
180
|
+
|
|
181
|
+
## 🔧 Advanced Usage
|
|
182
|
+
|
|
183
|
+
### Environment Variables
|
|
184
|
+
```bash
|
|
185
|
+
# Use different base URLs
|
|
186
|
+
OPENAI_BASE_URL="https://openrouter.ai/api/v1" tunacode
|
|
187
|
+
|
|
188
|
+
# Disable undo system
|
|
189
|
+
TUNACODE_NO_UNDO=1 tunacode
|
|
190
|
+
|
|
191
|
+
# Set default model
|
|
192
|
+
TUNACODE_MODEL="anthropic:claude-3-opus" tunacode
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### MCP Servers
|
|
196
|
+
Extend TunaCode with Model Context Protocol servers for web fetching, database access, and more. See [modelcontextprotocol.io](https://modelcontextprotocol.io/) for available servers.
|
|
197
|
+
|
|
198
|
+
## 🤝 Contributing
|
|
199
|
+
|
|
200
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Setup development environment
|
|
204
|
+
git clone https://github.com/larock22/tunacode
|
|
205
|
+
cd tunacode
|
|
206
|
+
pip install -e ".[dev]"
|
|
207
|
+
|
|
208
|
+
# Run tests
|
|
209
|
+
make test
|
|
210
|
+
|
|
211
|
+
# Lint code
|
|
212
|
+
make lint
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 📚 Documentation
|
|
216
|
+
|
|
217
|
+
- [Architecture Overview](docs/architecture.md)
|
|
218
|
+
- [API Integration](API_CALL_FLOW.md)
|
|
219
|
+
- [Undo System Design](UNDO_SYSTEM_DESIGN.md)
|
|
220
|
+
- [Performance Guide](PERFORMANCE_OPTIMIZATIONS.md)
|
|
221
|
+
|
|
222
|
+
## 🙏 Acknowledgments
|
|
223
|
+
|
|
224
|
+
TunaCode is built on the foundation of [sidekick-cli](https://github.com/geekforbrains/sidekick-cli). Special thanks to:
|
|
225
|
+
- The sidekick-cli team for the original codebase
|
|
226
|
+
- [TinyAgent](https://github.com/alchemiststudiosDOTai/tinyAgent) for the robust agent framework
|
|
227
|
+
- The open-source community for feedback and contributions
|
|
228
|
+
|
|
229
|
+
## 📄 License
|
|
230
|
+
|
|
231
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
**Note**: TunaCode is in active development. Please [report issues](https://github.com/larock22/tunacode/issues) or share feedback!
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# 🐟 TunaCode
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/tunacode-cli)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
**Your agentic CLI developer** - An open-source alternative to Claude Code, Copilot, and Cursor with multi-provider LLM support.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## ✨ What's New (v0.1.0)
|
|
13
|
+
|
|
14
|
+
- 🚀 **60% faster startup** with lazy loading and optimizations
|
|
15
|
+
- 🤖 **TinyAgent integration** for robust ReAct-based interactions
|
|
16
|
+
- 🛡️ **Three-layer undo system** with automatic failover
|
|
17
|
+
- 📊 **Enhanced model selection** with fuzzy matching and cost indicators
|
|
18
|
+
- 📁 **Project-local backups** in `.tunacode/` directory
|
|
19
|
+
|
|
20
|
+
## 🎯 Features
|
|
21
|
+
|
|
22
|
+
### Core Capabilities
|
|
23
|
+
- **🔓 No vendor lock-in** - Use any LLM provider (OpenAI, Anthropic, Google, 100+ via OpenRouter)
|
|
24
|
+
- **⚡ Fast & responsive** - Optimized for speed with <5ms operation overhead
|
|
25
|
+
- **🛡️ Safe operations** - Three-layer undo system ensures nothing is lost
|
|
26
|
+
- **🎨 Modern CLI** - Beautiful terminal UI with syntax highlighting
|
|
27
|
+
- **💰 Cost tracking** - Monitor tokens and costs per session
|
|
28
|
+
|
|
29
|
+
### Developer Experience
|
|
30
|
+
- **🔄 Hot model switching** - Change models mid-conversation with `/model`
|
|
31
|
+
- **📝 Project guides** - Customize behavior with `TUNACODE.md` files
|
|
32
|
+
- **🚀 YOLO mode** - Skip confirmations when you're confident
|
|
33
|
+
- **🔧 MCP support** - Extend with Model Context Protocol servers
|
|
34
|
+
- **📊 Git integration** - Automatic branch creation and undo support
|
|
35
|
+
|
|
36
|
+
## 🚀 Quick Start
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install from PyPI
|
|
40
|
+
pip install tunacode-cli
|
|
41
|
+
|
|
42
|
+
# Run setup (first time only)
|
|
43
|
+
tunacode
|
|
44
|
+
|
|
45
|
+
# Start coding!
|
|
46
|
+
tunacode
|
|
47
|
+
> Help me refactor this codebase to use async/await
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 📋 Commands
|
|
51
|
+
|
|
52
|
+
| Command | Description | Example |
|
|
53
|
+
|---------|-------------|---------|
|
|
54
|
+
| `/model` or `/m` | List and switch models | `/model 3` or `/m opus` |
|
|
55
|
+
| `/yolo` | Toggle confirmation skipping | `/yolo` |
|
|
56
|
+
| `/undo` | Undo last file operation | `/undo` |
|
|
57
|
+
| `/clear` | Clear conversation history | `/clear` |
|
|
58
|
+
| `/branch <name>` | Create new git branch | `/branch feature/auth` |
|
|
59
|
+
| `/compact` | Summarize and trim history | `/compact` |
|
|
60
|
+
| `/help` | Show all commands | `/help` |
|
|
61
|
+
|
|
62
|
+
## 🔧 Configuration
|
|
63
|
+
|
|
64
|
+
Configuration is stored in `~/.config/tunacode.json`:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"default_model": "openai:gpt-4o",
|
|
69
|
+
"env": {
|
|
70
|
+
"OPENAI_API_KEY": "sk-...",
|
|
71
|
+
"ANTHROPIC_API_KEY": "sk-ant-...",
|
|
72
|
+
"OPENROUTER_API_KEY": "sk-or-..."
|
|
73
|
+
},
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"github": {
|
|
76
|
+
"command": "npx",
|
|
77
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
78
|
+
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "..."}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Using OpenRouter (100+ Models)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Add your OpenRouter API key to config
|
|
88
|
+
# Then run with OpenRouter base URL:
|
|
89
|
+
OPENAI_BASE_URL="https://openrouter.ai/api/v1" tunacode
|
|
90
|
+
|
|
91
|
+
# Use any OpenRouter model:
|
|
92
|
+
/model openrouter:anthropic/claude-3-opus
|
|
93
|
+
/model openrouter:mistralai/devstral-small
|
|
94
|
+
/model openrouter:openai/gpt-4.1
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 🛡️ Undo System
|
|
98
|
+
|
|
99
|
+
TunaCode provides **three layers of protection** for your files:
|
|
100
|
+
|
|
101
|
+
1. **Git commits** - Primary undo mechanism (if available)
|
|
102
|
+
2. **Operation log** - Tracks changes with content (<100KB files)
|
|
103
|
+
3. **File backups** - Physical copies in `.tunacode/backups/`
|
|
104
|
+
|
|
105
|
+
All undo data is stored locally in your project:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
your-project/
|
|
109
|
+
└── .tunacode/ # Auto-created, gitignored
|
|
110
|
+
├── backups/ # Timestamped file copies
|
|
111
|
+
├── operations.jsonl # Change history
|
|
112
|
+
└── README.md # Explains the directory
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## 🎯 Project Customization
|
|
116
|
+
|
|
117
|
+
Create a `TUNACODE.md` file in your project root:
|
|
118
|
+
|
|
119
|
+
```markdown
|
|
120
|
+
# Project Guidelines for TunaCode
|
|
121
|
+
|
|
122
|
+
## Tech Stack
|
|
123
|
+
- Next.js 14 with App Router
|
|
124
|
+
- TypeScript with strict mode
|
|
125
|
+
- Tailwind CSS for styling
|
|
126
|
+
|
|
127
|
+
## Conventions
|
|
128
|
+
- Use arrow functions for components
|
|
129
|
+
- Prefer server components where possible
|
|
130
|
+
- Follow conventional commits
|
|
131
|
+
|
|
132
|
+
## Commands
|
|
133
|
+
- `npm run dev` - Start development
|
|
134
|
+
- `npm test` - Run tests
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## ⚡ Performance
|
|
138
|
+
|
|
139
|
+
TunaCode is optimized for speed:
|
|
140
|
+
- **Startup time**: ~0.5-0.8 seconds
|
|
141
|
+
- **Model switching**: ~100ms
|
|
142
|
+
- **File operations**: ~5ms overhead
|
|
143
|
+
- **API calls**: Connection pooling enabled
|
|
144
|
+
|
|
145
|
+
## 🔧 Advanced Usage
|
|
146
|
+
|
|
147
|
+
### Environment Variables
|
|
148
|
+
```bash
|
|
149
|
+
# Use different base URLs
|
|
150
|
+
OPENAI_BASE_URL="https://openrouter.ai/api/v1" tunacode
|
|
151
|
+
|
|
152
|
+
# Disable undo system
|
|
153
|
+
TUNACODE_NO_UNDO=1 tunacode
|
|
154
|
+
|
|
155
|
+
# Set default model
|
|
156
|
+
TUNACODE_MODEL="anthropic:claude-3-opus" tunacode
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### MCP Servers
|
|
160
|
+
Extend TunaCode with Model Context Protocol servers for web fetching, database access, and more. See [modelcontextprotocol.io](https://modelcontextprotocol.io/) for available servers.
|
|
161
|
+
|
|
162
|
+
## 🤝 Contributing
|
|
163
|
+
|
|
164
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Setup development environment
|
|
168
|
+
git clone https://github.com/larock22/tunacode
|
|
169
|
+
cd tunacode
|
|
170
|
+
pip install -e ".[dev]"
|
|
171
|
+
|
|
172
|
+
# Run tests
|
|
173
|
+
make test
|
|
174
|
+
|
|
175
|
+
# Lint code
|
|
176
|
+
make lint
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 📚 Documentation
|
|
180
|
+
|
|
181
|
+
- [Architecture Overview](docs/architecture.md)
|
|
182
|
+
- [API Integration](API_CALL_FLOW.md)
|
|
183
|
+
- [Undo System Design](UNDO_SYSTEM_DESIGN.md)
|
|
184
|
+
- [Performance Guide](PERFORMANCE_OPTIMIZATIONS.md)
|
|
185
|
+
|
|
186
|
+
## 🙏 Acknowledgments
|
|
187
|
+
|
|
188
|
+
TunaCode is built on the foundation of [sidekick-cli](https://github.com/geekforbrains/sidekick-cli). Special thanks to:
|
|
189
|
+
- The sidekick-cli team for the original codebase
|
|
190
|
+
- [TinyAgent](https://github.com/alchemiststudiosDOTai/tinyAgent) for the robust agent framework
|
|
191
|
+
- The open-source community for feedback and contributions
|
|
192
|
+
|
|
193
|
+
## 📄 License
|
|
194
|
+
|
|
195
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
**Note**: TunaCode is in active development. Please [report issues](https://github.com/larock22/tunacode/issues) or share feedback!
|
|
@@ -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.6"
|
|
8
8
|
description = "Your agentic CLI developer."
|
|
9
9
|
keywords = ["cli", "agent", "development", "automation"]
|
|
10
10
|
readme = "README.md"
|
|
@@ -26,10 +26,11 @@ classifiers = [
|
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
28
|
"prompt_toolkit==3.0.51",
|
|
29
|
-
"
|
|
29
|
+
"tiny_agent_os>=0.1.0",
|
|
30
30
|
"pygments==2.19.1",
|
|
31
31
|
"rich==14.0.0",
|
|
32
32
|
"typer==0.15.3",
|
|
33
|
+
"pyyaml>=6.0",
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
[project.scripts]
|
|
@@ -6,7 +6,6 @@ from enum import Enum
|
|
|
6
6
|
from typing import Any, Dict, List, Optional, Type
|
|
7
7
|
|
|
8
8
|
from .. import utils
|
|
9
|
-
from ..configuration.models import ModelRegistry
|
|
10
9
|
from ..exceptions import ValidationError
|
|
11
10
|
from ..services.undo_service import perform_undo
|
|
12
11
|
from ..types import CommandArgs, CommandContext, CommandResult, ProcessRequestCallback
|
|
@@ -260,7 +259,6 @@ class UndoCommand(SimpleCommand):
|
|
|
260
259
|
await ui.muted(" • File operations will still work, but can't be undone")
|
|
261
260
|
|
|
262
261
|
|
|
263
|
-
|
|
264
262
|
class BranchCommand(SimpleCommand):
|
|
265
263
|
"""Create and switch to a new git branch."""
|
|
266
264
|
|
|
@@ -388,46 +386,106 @@ class ModelCommand(SimpleCommand):
|
|
|
388
386
|
super().__init__(
|
|
389
387
|
CommandSpec(
|
|
390
388
|
name="model",
|
|
391
|
-
aliases=["/model"],
|
|
392
|
-
description="List
|
|
389
|
+
aliases=["/model", "/m"],
|
|
390
|
+
description="List and select AI models interactively",
|
|
393
391
|
category=CommandCategory.MODEL,
|
|
394
392
|
)
|
|
395
393
|
)
|
|
396
394
|
|
|
397
395
|
async def execute(self, args: CommandArgs, context: CommandContext) -> Optional[str]:
|
|
396
|
+
from tunacode.cli.model_selector import ModelSelector
|
|
397
|
+
|
|
398
|
+
selector = ModelSelector()
|
|
399
|
+
|
|
398
400
|
if not args:
|
|
399
|
-
# No arguments - list
|
|
400
|
-
await
|
|
401
|
+
# No arguments - show enhanced model list
|
|
402
|
+
await self._show_model_list(selector, context.state_manager)
|
|
401
403
|
return None
|
|
402
404
|
|
|
403
|
-
#
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
except ValueError:
|
|
407
|
-
await ui.error(f"Invalid model index: {args[0]}")
|
|
408
|
-
return None
|
|
405
|
+
# Find model by query (index, name, or fuzzy match)
|
|
406
|
+
query = args[0]
|
|
407
|
+
model_info = selector.find_model(query)
|
|
409
408
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
409
|
+
if not model_info:
|
|
410
|
+
# Try to provide helpful suggestions
|
|
411
|
+
await ui.error(f"Model '{query}' not found")
|
|
412
|
+
await ui.muted(
|
|
413
|
+
"Try: /model (to list all), or use a number 0-18, "
|
|
414
|
+
"or model name like 'opus' or 'gpt-4'"
|
|
415
|
+
)
|
|
415
416
|
return None
|
|
416
417
|
|
|
417
418
|
# Set the model
|
|
418
|
-
|
|
419
|
-
context.state_manager.session.current_model = model
|
|
419
|
+
context.state_manager.session.current_model = model_info.id
|
|
420
420
|
|
|
421
421
|
# Check if setting as default
|
|
422
422
|
if len(args) > 1 and args[1] == "default":
|
|
423
|
-
utils.user_configuration.set_default_model(
|
|
424
|
-
await ui.
|
|
423
|
+
utils.user_configuration.set_default_model(model_info.id, context.state_manager)
|
|
424
|
+
await ui.success(
|
|
425
|
+
f"Set default model: {model_info.display_name} {model_info.provider.value[2]}"
|
|
426
|
+
)
|
|
425
427
|
return "restart"
|
|
426
428
|
else:
|
|
427
|
-
# Show success message with
|
|
428
|
-
|
|
429
|
+
# Show success message with model details
|
|
430
|
+
cost_emoji = selector.get_cost_emoji(model_info.cost_tier)
|
|
431
|
+
await ui.success(
|
|
432
|
+
f"Switched to: {model_info.display_name} "
|
|
433
|
+
f"{model_info.provider.value[2]} {cost_emoji}\n"
|
|
434
|
+
f" → {model_info.description}"
|
|
435
|
+
)
|
|
429
436
|
return None
|
|
430
437
|
|
|
438
|
+
async def _show_model_list(self, selector, state_manager) -> None:
|
|
439
|
+
"""Show enhanced model list grouped by provider."""
|
|
440
|
+
from rich.table import Table
|
|
441
|
+
from rich.text import Text
|
|
442
|
+
|
|
443
|
+
# Create table
|
|
444
|
+
table = Table(show_header=True, box=None, padding=(0, 2))
|
|
445
|
+
table.add_column("ID", style="dim", width=3)
|
|
446
|
+
table.add_column("Model", style="bold")
|
|
447
|
+
table.add_column("Short", style="cyan")
|
|
448
|
+
table.add_column("Description", style="dim")
|
|
449
|
+
table.add_column("Cost", justify="center", width=4)
|
|
450
|
+
|
|
451
|
+
# Current model
|
|
452
|
+
current_model = state_manager.session.current_model if state_manager else None
|
|
453
|
+
|
|
454
|
+
# Add models grouped by provider
|
|
455
|
+
model_index = 0
|
|
456
|
+
grouped = selector.get_models_by_provider()
|
|
457
|
+
|
|
458
|
+
for provider in [p for p in grouped if grouped[p]]: # Only show providers with models
|
|
459
|
+
# Add provider header
|
|
460
|
+
table.add_row(
|
|
461
|
+
"",
|
|
462
|
+
Text(f"{provider.value[2]} {provider.value[1]}", style="bold magenta"),
|
|
463
|
+
"",
|
|
464
|
+
"",
|
|
465
|
+
"",
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# Add models for this provider
|
|
469
|
+
for model in grouped[provider]:
|
|
470
|
+
is_current = model.id == current_model
|
|
471
|
+
style = "bold green" if is_current else ""
|
|
472
|
+
|
|
473
|
+
table.add_row(
|
|
474
|
+
str(model_index),
|
|
475
|
+
Text(model.display_name + (" ← current" if is_current else ""), style=style),
|
|
476
|
+
model.short_name,
|
|
477
|
+
model.description,
|
|
478
|
+
selector.get_cost_emoji(model.cost_tier),
|
|
479
|
+
)
|
|
480
|
+
model_index += 1
|
|
481
|
+
|
|
482
|
+
# Show the table
|
|
483
|
+
await ui.panel("Available Models", table, border_style="cyan")
|
|
484
|
+
|
|
485
|
+
# Show usage hints
|
|
486
|
+
await ui.muted("\n💡 Usage: /model <number|name> [default]")
|
|
487
|
+
await ui.muted(" Examples: /model 3, /model opus, /model gpt-4 default")
|
|
488
|
+
|
|
431
489
|
|
|
432
490
|
@dataclass
|
|
433
491
|
class CommandDependencies:
|
|
@@ -488,8 +546,7 @@ class CommandRegistry:
|
|
|
488
546
|
category_commands = self._categories[command.category]
|
|
489
547
|
# Remove any existing instance of this command class
|
|
490
548
|
self._categories[command.category] = [
|
|
491
|
-
cmd for cmd in category_commands
|
|
492
|
-
if cmd.__class__ != command.__class__
|
|
549
|
+
cmd for cmd in category_commands if cmd.__class__ != command.__class__
|
|
493
550
|
]
|
|
494
551
|
# Add the new instance
|
|
495
552
|
self._categories[command.category].append(command)
|
|
@@ -533,7 +590,7 @@ class CommandRegistry:
|
|
|
533
590
|
# Only update if callback has changed
|
|
534
591
|
if self._factory.dependencies.process_request_callback == callback:
|
|
535
592
|
return
|
|
536
|
-
|
|
593
|
+
|
|
537
594
|
self._factory.update_dependencies(process_request_callback=callback)
|
|
538
595
|
|
|
539
596
|
# Re-register CompactCommand with new dependency if already registered
|
|
@@ -568,10 +625,10 @@ class CommandRegistry:
|
|
|
568
625
|
if command_name in self._commands:
|
|
569
626
|
command = self._commands[command_name]
|
|
570
627
|
return await command.execute(args, context)
|
|
571
|
-
|
|
628
|
+
|
|
572
629
|
# Try partial matching
|
|
573
630
|
matches = self.find_matching_commands(command_name)
|
|
574
|
-
|
|
631
|
+
|
|
575
632
|
if not matches:
|
|
576
633
|
raise ValidationError(f"Unknown command: {command_name}")
|
|
577
634
|
elif len(matches) == 1:
|
|
@@ -581,16 +638,17 @@ class CommandRegistry:
|
|
|
581
638
|
else:
|
|
582
639
|
# Ambiguous - show possibilities
|
|
583
640
|
raise ValidationError(
|
|
584
|
-
f"Ambiguous command '{command_name}'. Did you mean:
|
|
641
|
+
f"Ambiguous command '{command_name}'. Did you mean: "
|
|
642
|
+
f"{', '.join(sorted(set(matches)))}?"
|
|
585
643
|
)
|
|
586
644
|
|
|
587
645
|
def find_matching_commands(self, partial_command: str) -> List[str]:
|
|
588
646
|
"""
|
|
589
647
|
Find all commands that start with the given partial command.
|
|
590
|
-
|
|
648
|
+
|
|
591
649
|
Args:
|
|
592
650
|
partial_command: The partial command to match
|
|
593
|
-
|
|
651
|
+
|
|
594
652
|
Returns:
|
|
595
653
|
List of matching command names
|
|
596
654
|
"""
|
|
@@ -608,11 +666,11 @@ class CommandRegistry:
|
|
|
608
666
|
return False
|
|
609
667
|
|
|
610
668
|
command_name = parts[0].lower()
|
|
611
|
-
|
|
669
|
+
|
|
612
670
|
# Check exact match first
|
|
613
671
|
if command_name in self._commands:
|
|
614
672
|
return True
|
|
615
|
-
|
|
673
|
+
|
|
616
674
|
# Check partial match
|
|
617
675
|
return len(self.find_matching_commands(command_name)) > 0
|
|
618
676
|
|