tunacode-cli 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- tunacode/cli/commands.py +91 -33
- tunacode/cli/model_selector.py +178 -0
- tunacode/cli/repl.py +11 -10
- tunacode/configuration/models.py +11 -1
- tunacode/constants.py +11 -11
- tunacode/context.py +1 -3
- tunacode/core/agents/main.py +52 -94
- tunacode/core/agents/tinyagent_main.py +171 -0
- tunacode/core/setup/git_safety_setup.py +39 -51
- tunacode/core/setup/optimized_coordinator.py +73 -0
- tunacode/exceptions.py +13 -15
- tunacode/services/enhanced_undo_service.py +322 -0
- tunacode/services/project_undo_service.py +311 -0
- tunacode/services/undo_service.py +18 -21
- tunacode/tools/base.py +11 -20
- tunacode/tools/tinyagent_tools.py +103 -0
- tunacode/tools/update_file.py +24 -14
- tunacode/tools/write_file.py +9 -7
- tunacode/types.py +2 -2
- tunacode/ui/completers.py +98 -33
- tunacode/ui/input.py +8 -7
- tunacode/ui/keybindings.py +1 -3
- tunacode/ui/lexers.py +16 -17
- tunacode/ui/output.py +9 -3
- tunacode/ui/panels.py +4 -4
- tunacode/ui/prompt_manager.py +6 -4
- tunacode/utils/lazy_imports.py +59 -0
- tunacode/utils/regex_cache.py +33 -0
- tunacode/utils/system.py +13 -13
- tunacode_cli-0.0.6.dist-info/METADATA +235 -0
- {tunacode_cli-0.0.4.dist-info → tunacode_cli-0.0.6.dist-info}/RECORD +35 -27
- tunacode_cli-0.0.4.dist-info/METADATA +0 -247
- {tunacode_cli-0.0.4.dist-info → tunacode_cli-0.0.6.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.4.dist-info → tunacode_cli-0.0.6.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.4.dist-info → tunacode_cli-0.0.6.dist-info}/licenses/LICENSE +0 -0
- {tunacode_cli-0.0.4.dist-info → tunacode_cli-0.0.6.dist-info}/top_level.txt +0 -0
tunacode/ui/lexers.py
CHANGED
|
@@ -2,45 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from prompt_toolkit.formatted_text import FormattedText
|
|
6
5
|
from prompt_toolkit.lexers import Lexer
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class FileReferenceLexer(Lexer):
|
|
10
9
|
"""Lexer that highlights @file references in light blue."""
|
|
11
|
-
|
|
10
|
+
|
|
12
11
|
# Pattern to match @file references
|
|
13
|
-
FILE_REF_PATTERN = re.compile(r
|
|
14
|
-
|
|
12
|
+
FILE_REF_PATTERN = re.compile(r"@([\w./_-]+)")
|
|
13
|
+
|
|
15
14
|
def lex_document(self, document):
|
|
16
15
|
"""Return a formatted text list for the given document."""
|
|
17
|
-
lines = document.text.split(
|
|
18
|
-
|
|
16
|
+
lines = document.text.split("\n")
|
|
17
|
+
|
|
19
18
|
def get_line_tokens(line_number):
|
|
20
19
|
"""Get tokens for a specific line."""
|
|
21
20
|
if line_number >= len(lines):
|
|
22
21
|
return []
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
line = lines[line_number]
|
|
25
24
|
tokens = []
|
|
26
25
|
last_end = 0
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Find all @file references in the line
|
|
29
28
|
for match in self.FILE_REF_PATTERN.finditer(line):
|
|
30
29
|
start, end = match.span()
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
# Add text before the match
|
|
33
32
|
if start > last_end:
|
|
34
|
-
tokens.append((
|
|
35
|
-
|
|
33
|
+
tokens.append(("", line[last_end:start]))
|
|
34
|
+
|
|
36
35
|
# Add the @file reference with styling
|
|
37
|
-
tokens.append((
|
|
36
|
+
tokens.append(("class:file-reference", match.group(0)))
|
|
38
37
|
last_end = end
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
# Add remaining text
|
|
41
40
|
if last_end < len(line):
|
|
42
|
-
tokens.append((
|
|
43
|
-
|
|
41
|
+
tokens.append(("", line[last_end:]))
|
|
42
|
+
|
|
44
43
|
return tokens
|
|
45
|
-
|
|
46
|
-
return get_line_tokens
|
|
44
|
+
|
|
45
|
+
return get_line_tokens
|
tunacode/ui/output.py
CHANGED
|
@@ -16,9 +16,15 @@ from .decorators import create_sync_wrapper
|
|
|
16
16
|
console = Console()
|
|
17
17
|
colors = DotDict(UI_COLORS)
|
|
18
18
|
|
|
19
|
-
BANNER =
|
|
20
|
-
[bold #00d7ff]
|
|
21
|
-
[
|
|
19
|
+
BANNER = (
|
|
20
|
+
"[bold #00d7ff]┌─────────────────────────────────────────────────────────────────┐"
|
|
21
|
+
"[/bold #00d7ff]\n"
|
|
22
|
+
"[bold #00d7ff]│[/bold #00d7ff] [bold white]T U N A C O D E[/bold white] "
|
|
23
|
+
"[dim #64748b]• Agentic AI Development Environment[/dim #64748b] "
|
|
24
|
+
"[bold #00d7ff]│[/bold #00d7ff]\n"
|
|
25
|
+
"[bold #00d7ff]└─────────────────────────────────────────────────────────────────┘"
|
|
26
|
+
"[/bold #00d7ff]"
|
|
27
|
+
)
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
@create_sync_wrapper
|
tunacode/ui/panels.py
CHANGED
|
@@ -38,11 +38,11 @@ async def panel(
|
|
|
38
38
|
"""Display a rich panel with modern styling."""
|
|
39
39
|
border_style = border_style or kwargs.get("style") or colors.border
|
|
40
40
|
panel_obj = Panel(
|
|
41
|
-
Padding(text, (0, 1, 0, 1)),
|
|
42
|
-
title=f"[bold]{title}[/bold]",
|
|
43
|
-
title_align="left",
|
|
41
|
+
Padding(text, (0, 1, 0, 1)),
|
|
42
|
+
title=f"[bold]{title}[/bold]",
|
|
43
|
+
title_align="left",
|
|
44
44
|
border_style=border_style,
|
|
45
|
-
padding=(0, 1)
|
|
45
|
+
padding=(0, 1),
|
|
46
46
|
)
|
|
47
47
|
await print(Padding(panel_obj, (top, right, bottom, left)), **kwargs)
|
|
48
48
|
|
tunacode/ui/prompt_manager.py
CHANGED
|
@@ -42,12 +42,14 @@ class PromptManager:
|
|
|
42
42
|
self.state_manager = state_manager
|
|
43
43
|
self._temp_sessions = {} # For when no state manager is available
|
|
44
44
|
self._style = self._create_style()
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
def _create_style(self) -> Style:
|
|
47
47
|
"""Create the style for the prompt with file reference highlighting."""
|
|
48
|
-
return Style.from_dict(
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
return Style.from_dict(
|
|
49
|
+
{
|
|
50
|
+
"file-reference": UI_COLORS.get("file_ref", "light_blue"),
|
|
51
|
+
}
|
|
52
|
+
)
|
|
51
53
|
|
|
52
54
|
def get_session(self, session_key: str, config: PromptConfig) -> PromptSession:
|
|
53
55
|
"""Get or create a prompt session.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Lazy imports for heavy modules to improve startup time."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
# For type checking only
|
|
8
|
+
import prompt_toolkit # noqa: F401
|
|
9
|
+
import rich # noqa: F401
|
|
10
|
+
from prompt_toolkit import PromptSession # noqa: F401
|
|
11
|
+
from prompt_toolkit.completion import Completer # noqa: F401
|
|
12
|
+
from rich.console import Console # noqa: F401
|
|
13
|
+
from rich.markdown import Markdown # noqa: F401
|
|
14
|
+
from rich.panel import Panel # noqa: F401
|
|
15
|
+
from rich.table import Table # noqa: F401
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def lazy_import(module_name: str):
|
|
19
|
+
"""Lazy import a module."""
|
|
20
|
+
if module_name not in sys.modules:
|
|
21
|
+
__import__(module_name)
|
|
22
|
+
return sys.modules[module_name]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Lazy accessors
|
|
26
|
+
def get_rich():
|
|
27
|
+
"""Get rich module lazily."""
|
|
28
|
+
return lazy_import("rich")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_rich_console():
|
|
32
|
+
"""Get rich console lazily."""
|
|
33
|
+
rich_console = lazy_import("rich.console")
|
|
34
|
+
return rich_console.Console
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_rich_table():
|
|
38
|
+
"""Get rich table lazily."""
|
|
39
|
+
return lazy_import("rich.table").Table
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_rich_panel():
|
|
43
|
+
"""Get rich panel lazily."""
|
|
44
|
+
return lazy_import("rich.panel").Panel
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_rich_markdown():
|
|
48
|
+
"""Get rich markdown lazily."""
|
|
49
|
+
return lazy_import("rich.markdown").Markdown
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_prompt_toolkit():
|
|
53
|
+
"""Get prompt_toolkit lazily."""
|
|
54
|
+
return lazy_import("prompt_toolkit")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_prompt_session():
|
|
58
|
+
"""Get PromptSession lazily."""
|
|
59
|
+
return lazy_import("prompt_toolkit").PromptSession
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Pre-compiled regex patterns for better performance."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
# Command patterns
|
|
6
|
+
MODEL_COMMAND_PATTERN = re.compile(r"(?:^|\n)\s*(?:/model|/m)\s+\S*$")
|
|
7
|
+
COMMAND_START_PATTERN = re.compile(r"^/\w+")
|
|
8
|
+
|
|
9
|
+
# File reference patterns
|
|
10
|
+
FILE_REF_PATTERN = re.compile(r"@([^\s]+)")
|
|
11
|
+
FILE_PATH_PATTERN = re.compile(r"^[a-zA-Z0-9_\-./]+$")
|
|
12
|
+
|
|
13
|
+
# Code patterns
|
|
14
|
+
IMPORT_PATTERN = re.compile(r"^\s*(?:from|import)\s+\S+")
|
|
15
|
+
FUNCTION_DEF_PATTERN = re.compile(r"^\s*def\s+(\w+)\s*\(")
|
|
16
|
+
CLASS_DEF_PATTERN = re.compile(r"^\s*class\s+(\w+)")
|
|
17
|
+
|
|
18
|
+
# Environment variable patterns
|
|
19
|
+
ENV_VAR_PATTERN = re.compile(r"\$\{(\w+)(?::([^}]*))?\}")
|
|
20
|
+
API_KEY_PATTERN = re.compile(r"_API_KEY$")
|
|
21
|
+
|
|
22
|
+
# Common text patterns
|
|
23
|
+
WHITESPACE_PATTERN = re.compile(r"\s+")
|
|
24
|
+
WORD_BOUNDARY_PATTERN = re.compile(r"\b\w+\b")
|
|
25
|
+
LINE_SPLIT_PATTERN = re.compile(r"\r?\n")
|
|
26
|
+
|
|
27
|
+
# Tool output patterns
|
|
28
|
+
ANSI_ESCAPE_PATTERN = re.compile(r"\x1b\[[0-9;]*m")
|
|
29
|
+
ERROR_PATTERN = re.compile(r"(?i)(error|exception|failed|failure):\s*(.+)")
|
|
30
|
+
|
|
31
|
+
# Model name patterns
|
|
32
|
+
MODEL_PROVIDER_PATTERN = re.compile(r"^(\w+):(.+)$")
|
|
33
|
+
OPENROUTER_MODEL_PATTERN = re.compile(r"^openrouter:(.+)$")
|
tunacode/utils/system.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Module:
|
|
2
|
+
Module: tunacode.utils.system
|
|
3
3
|
|
|
4
4
|
Provides system information and directory management utilities.
|
|
5
5
|
Handles session management, device identification, file listing
|
|
@@ -13,7 +13,7 @@ import uuid
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
from ..configuration.settings import ApplicationSettings
|
|
16
|
-
from ..constants import DEVICE_ID_FILE, ENV_FILE, SESSIONS_SUBDIR,
|
|
16
|
+
from ..constants import DEVICE_ID_FILE, ENV_FILE, SESSIONS_SUBDIR, TUNACODE_HOME_DIR
|
|
17
17
|
|
|
18
18
|
# Default ignore patterns if .gitignore is not found
|
|
19
19
|
DEFAULT_IGNORE_PATTERNS = {
|
|
@@ -45,15 +45,15 @@ DEFAULT_IGNORE_PATTERNS = {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def get_tunacode_home():
|
|
49
49
|
"""
|
|
50
|
-
Get the path to the
|
|
50
|
+
Get the path to the TunaCode home directory (~/.tunacode).
|
|
51
51
|
Creates it if it doesn't exist.
|
|
52
52
|
|
|
53
53
|
Returns:
|
|
54
|
-
Path: The path to the
|
|
54
|
+
Path: The path to the TunaCode home directory.
|
|
55
55
|
"""
|
|
56
|
-
home = Path.home() /
|
|
56
|
+
home = Path.home() / TUNACODE_HOME_DIR
|
|
57
57
|
home.mkdir(exist_ok=True)
|
|
58
58
|
return home
|
|
59
59
|
|
|
@@ -68,7 +68,7 @@ def get_session_dir(state_manager):
|
|
|
68
68
|
Returns:
|
|
69
69
|
Path: The path to the current session directory.
|
|
70
70
|
"""
|
|
71
|
-
session_dir =
|
|
71
|
+
session_dir = get_tunacode_home() / SESSIONS_SUBDIR / state_manager.session.session_id
|
|
72
72
|
session_dir.mkdir(exist_ok=True, parents=True)
|
|
73
73
|
return session_dir
|
|
74
74
|
|
|
@@ -181,16 +181,16 @@ def get_cwd():
|
|
|
181
181
|
|
|
182
182
|
def get_device_id():
|
|
183
183
|
"""
|
|
184
|
-
Get the device ID from the ~/.
|
|
184
|
+
Get the device ID from the ~/.tunacode/device_id file.
|
|
185
185
|
If the file doesn't exist, generate a new UUID and save it.
|
|
186
186
|
|
|
187
187
|
Returns:
|
|
188
188
|
str: The device ID as a string.
|
|
189
189
|
"""
|
|
190
190
|
try:
|
|
191
|
-
# Get the ~/.
|
|
192
|
-
|
|
193
|
-
device_id_file =
|
|
191
|
+
# Get the ~/.tunacode directory
|
|
192
|
+
tunacode_home = get_tunacode_home()
|
|
193
|
+
device_id_file = tunacode_home / DEVICE_ID_FILE
|
|
194
194
|
|
|
195
195
|
# If the file exists, read the device ID
|
|
196
196
|
if device_id_file.exists():
|
|
@@ -245,7 +245,7 @@ def cleanup_session(state_manager):
|
|
|
245
245
|
|
|
246
246
|
def check_for_updates():
|
|
247
247
|
"""
|
|
248
|
-
Check if there's a newer version of
|
|
248
|
+
Check if there's a newer version of tunacode-cli available on PyPI.
|
|
249
249
|
|
|
250
250
|
Returns:
|
|
251
251
|
tuple: (has_update, latest_version)
|
|
@@ -257,7 +257,7 @@ def check_for_updates():
|
|
|
257
257
|
current_version = app_settings.version
|
|
258
258
|
try:
|
|
259
259
|
result = subprocess.run(
|
|
260
|
-
["pip", "index", "versions", "
|
|
260
|
+
["pip", "index", "versions", "tunacode-cli"], capture_output=True, text=True, check=True
|
|
261
261
|
)
|
|
262
262
|
output = result.stdout
|
|
263
263
|
|
|
@@ -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!
|
|
@@ -1,65 +1,73 @@
|
|
|
1
1
|
tunacode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
tunacode/constants.py,sha256=
|
|
3
|
-
tunacode/context.py,sha256=
|
|
4
|
-
tunacode/exceptions.py,sha256=
|
|
2
|
+
tunacode/constants.py,sha256=6QxZ_VpAzjWSx5-tndEUjTeUVSrNSTAlWSRuboqr-DQ,4218
|
|
3
|
+
tunacode/context.py,sha256=6sterdRvPOyG3LU0nEAXpBsEPZbO3qtPyTlJBi-_VXE,2612
|
|
4
|
+
tunacode/exceptions.py,sha256=_Dyj6cC0868dMABekdQHXCg5XhucJumbGUMXaSDzgB4,2645
|
|
5
5
|
tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
tunacode/setup.py,sha256=KCNhnu8FY5bFRqR0JMIh22xVakCC97kchmyGsUGUaJ4,1811
|
|
7
|
-
tunacode/types.py,sha256
|
|
7
|
+
tunacode/types.py,sha256=5mMJDgFqVcKzhtHh9unPISBFqkeNje6KISGUpRkqRjY,7146
|
|
8
8
|
tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
|
|
9
|
-
tunacode/cli/commands.py,sha256=
|
|
9
|
+
tunacode/cli/commands.py,sha256=IHUza0gJUrN30sUHK9gU6-cSRV0TufViBU8qq0yzYbI,22979
|
|
10
10
|
tunacode/cli/main.py,sha256=5_CXYtzN-Mc3nOv2Xpk6yfnV4d2SOgA9ENyTCe0cYYw,1226
|
|
11
|
-
tunacode/cli/
|
|
11
|
+
tunacode/cli/model_selector.py,sha256=EO3GY_YM6pByn4IuYYNn5bKQUCz6vaObFw7clJaJQD8,6190
|
|
12
|
+
tunacode/cli/repl.py,sha256=-KvGMyYQk4WslSkGk0MVOUCB15DEp0BSPVWGj4TIZlo,8864
|
|
12
13
|
tunacode/configuration/__init__.py,sha256=MbVXy8bGu0yKehzgdgZ_mfWlYGvIdb1dY2Ly75nfuPE,17
|
|
13
14
|
tunacode/configuration/defaults.py,sha256=9fR_Wydc85fU5LN5LlgRfW6geZNPNFMqKRVLN2v_TQ8,694
|
|
14
|
-
tunacode/configuration/models.py,sha256=
|
|
15
|
+
tunacode/configuration/models.py,sha256=hcpO_Z0fy6kVf2xjv-Azs2PMQE-iKwxIQhpi2KuvTMs,3944
|
|
15
16
|
tunacode/configuration/settings.py,sha256=0HqwEFkpGqPtF-h0sAIBHFXTlJQ_KtelD_s-z2XDSak,992
|
|
16
17
|
tunacode/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
18
|
tunacode/core/state.py,sha256=0U_WU92yn5EQ27BLlHIkNIJJqjLMNHKNYSoba1rQqbQ,1376
|
|
18
19
|
tunacode/core/tool_handler.py,sha256=OKx7jM8pml6pSEnoARu33_uBY8awJBqvhbVeBn6T4ow,1804
|
|
19
20
|
tunacode/core/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
tunacode/core/agents/main.py,sha256=
|
|
21
|
+
tunacode/core/agents/main.py,sha256=zsfSmUUHNFfEz9ludq9CZ13qaGJn91TrOyu1Pha_3-M,2339
|
|
22
|
+
tunacode/core/agents/tinyagent_main.py,sha256=Yk0Fc_MKGJQqo-MIVGk8Q30Jnx4lumtF59O-eu_vuzw,5785
|
|
21
23
|
tunacode/core/setup/__init__.py,sha256=jlveyriTXRcnoBLU6_TJ7Z-3E6EXjT9L5GD1vW4dei0,427
|
|
22
24
|
tunacode/core/setup/agent_setup.py,sha256=k1bmQ21ue17_zZsWlhIfc7ZWN6vZtmOgKMMzR4Xu9-k,1410
|
|
23
25
|
tunacode/core/setup/base.py,sha256=x9uYOITulrf4faP70NPTNBPb-wW1ZJGmcjAe0Sk5slk,961
|
|
24
26
|
tunacode/core/setup/config_setup.py,sha256=1eDS8iIG9z_BvrmeL8d9QUEBtPXSjOcrNu-2vHtvSFk,7474
|
|
25
27
|
tunacode/core/setup/coordinator.py,sha256=pT1mI24Kllo2H6Hjb-uULLsA1E3X3BqQB7nSGe70SRw,1598
|
|
26
28
|
tunacode/core/setup/environment_setup.py,sha256=u6Anb9uDicmdMA_9xLkP0j2uoiVY16ojsA7XnFz--yQ,2234
|
|
27
|
-
tunacode/core/setup/git_safety_setup.py,sha256=
|
|
29
|
+
tunacode/core/setup/git_safety_setup.py,sha256=43NqLHxUxzG5GAI0lgtU3cyIoV2izfEEe8zWig_GosE,6910
|
|
30
|
+
tunacode/core/setup/optimized_coordinator.py,sha256=pRfX0AUjBfPE8jdl7lNJwnnTOEX4p1aBdWT92DGhgNY,2936
|
|
28
31
|
tunacode/core/setup/undo_setup.py,sha256=uHy7YWXPnxQ-b_36SNKtMZNyDR_u-p4OPDARHAgp6Lo,1179
|
|
29
32
|
tunacode/prompts/system.txt,sha256=freAiukJH4PN-fKRWBdTogEak7BsZMgwdyWkxQtY-X4,3194
|
|
30
33
|
tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
|
|
34
|
+
tunacode/services/enhanced_undo_service.py,sha256=_HGY0v7uwXajelElhbQGij2oKhIol5NxMzop841XJxY,10303
|
|
31
35
|
tunacode/services/mcp.py,sha256=R48X73KQjQ9vwhBrtbWHSBl-4K99QXmbIhh5J_1Gezo,3046
|
|
32
|
-
tunacode/services/
|
|
36
|
+
tunacode/services/project_undo_service.py,sha256=slZPqN4LYyiT6vSKaLcsM5eOceIrZ4UPM-ce9nJZ1AQ,10648
|
|
37
|
+
tunacode/services/undo_service.py,sha256=CSjpwb53n9U5cVzPxRnxG6RWiD8motwwNb9Mn0Rc6cg,7702
|
|
33
38
|
tunacode/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
tunacode/tools/base.py,sha256=
|
|
39
|
+
tunacode/tools/base.py,sha256=Cl-xthjTt6mlzvGsgmI-64m_8PCM1W8lccqsSemPq0k,8478
|
|
35
40
|
tunacode/tools/read_file.py,sha256=QW97pPVcHvNJk48iPSwzOZ9Dbq_Ce8lQ7W0F82SXi7k,3051
|
|
36
41
|
tunacode/tools/run_command.py,sha256=dspFV71g98gbOQV6CT9LsEoytQ_4zyVqqZD99zf__OY,3796
|
|
37
|
-
tunacode/tools/
|
|
38
|
-
tunacode/tools/
|
|
42
|
+
tunacode/tools/tinyagent_tools.py,sha256=9JyTZgcvk2HVV6Q8UCS4tr0HPU6T2OMfS7AHJaMBgSw,2590
|
|
43
|
+
tunacode/tools/update_file.py,sha256=-ysza8w3eys1jj-oDFsamXiSVI28ONTkMQ4TJsoG6xs,4527
|
|
44
|
+
tunacode/tools/write_file.py,sha256=taDr8nAnxYeEXz6W3tjzT_S96u2MiHD1puvJFfYxlbw,2919
|
|
39
45
|
tunacode/ui/__init__.py,sha256=aRNE2pS50nFAX6y--rSGMNYwhz905g14gRd6g4BolYU,13
|
|
40
|
-
tunacode/ui/completers.py,sha256=
|
|
46
|
+
tunacode/ui/completers.py,sha256=OL4c8cSQnb2WWRfD73MkRBw1lOxa03512MKDFPli9eY,7231
|
|
41
47
|
tunacode/ui/console.py,sha256=7ua1LaBP3ZCRHzV0SqVCkgNlgBuBcBTcvybRjWl5jew,1943
|
|
42
48
|
tunacode/ui/constants.py,sha256=NxegAWaoDaEa4gzNZ7p67M0ZsHJJxZMtf2bW8E5WeZE,421
|
|
43
49
|
tunacode/ui/decorators.py,sha256=I09tETtxPfcWdh1B3kEM0nJ8E6HvdBZAdYhRzYUa_p8,1901
|
|
44
|
-
tunacode/ui/input.py,sha256=
|
|
45
|
-
tunacode/ui/keybindings.py,sha256=
|
|
46
|
-
tunacode/ui/lexers.py,sha256=
|
|
47
|
-
tunacode/ui/output.py,sha256=
|
|
48
|
-
tunacode/ui/panels.py,sha256=
|
|
49
|
-
tunacode/ui/prompt_manager.py,sha256=
|
|
50
|
+
tunacode/ui/input.py,sha256=aiM5Kv2CI0Vqj05srR2R2Z4e4dZ8QY6yoNKQ04RlQhg,2909
|
|
51
|
+
tunacode/ui/keybindings.py,sha256=Tiw7L57Q-I7mc-qdE1aWVUxa4TCHgO4niUdoRvzO-6o,691
|
|
52
|
+
tunacode/ui/lexers.py,sha256=tmg4ic1enyTRLzanN5QPP7D_0n12YjX_8ZhsffzhXA4,1340
|
|
53
|
+
tunacode/ui/output.py,sha256=L7VdlwbxqiOlQLPvcty76zX2ImXAtSioK1w_BO5F7Hc,3859
|
|
54
|
+
tunacode/ui/panels.py,sha256=kuhCPvLwqorrqyS0ZF-RfuzbDZHwhKOC7KG2wQOqXds,5931
|
|
55
|
+
tunacode/ui/prompt_manager.py,sha256=uqmS796nXn7me-zcATyK8A_dz0M037zkPYhMZMbI6jI,4036
|
|
50
56
|
tunacode/ui/tool_ui.py,sha256=XqJhVm3NNGwvIbMYQ8JmmLwjGeIT3WHCf5INrgBfpY8,6530
|
|
51
57
|
tunacode/ui/validators.py,sha256=wkg-lQrP-Wjm5phUHKD3Mte8F1J3A2EjESQgPPtRcvQ,758
|
|
52
58
|
tunacode/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
59
|
tunacode/utils/bm25.py,sha256=yq7KFWP3g_zIsjUV7l2hFPXYCzXyNQUInLU7u4qsc_4,1909
|
|
54
60
|
tunacode/utils/diff_utils.py,sha256=V9PM1Of0wvWNTVyw7iZYrNowFuRiKSyWiw5F39yRRYA,2394
|
|
55
61
|
tunacode/utils/file_utils.py,sha256=AXiAJ_idtlmXEi9pMvwtfPy9Ys3yK-F4K7qb_NpwonU,923
|
|
62
|
+
tunacode/utils/lazy_imports.py,sha256=O5t6U1OaI7Ody69wPELQBh51aDyA5VxfLohZiD12oLY,1501
|
|
63
|
+
tunacode/utils/regex_cache.py,sha256=vuB7c1HbZxcRKCE4R3DiOYvTpF1Nj4bcxW5nNEiOEAw,1093
|
|
56
64
|
tunacode/utils/ripgrep.py,sha256=AXUs2FFt0A7KBV996deS8wreIlUzKOlAHJmwrcAr4No,583
|
|
57
|
-
tunacode/utils/system.py,sha256=
|
|
65
|
+
tunacode/utils/system.py,sha256=FSoibTIH0eybs4oNzbYyufIiV6gb77QaeY2yGqW39AY,11381
|
|
58
66
|
tunacode/utils/text_utils.py,sha256=B9M1cuLTm_SSsr15WNHF6j7WdLWPvWzKZV0Lvfgdbjg,2458
|
|
59
67
|
tunacode/utils/user_configuration.py,sha256=uFrpSRTUf0CijZjw1rOp1sovqy1uyr0UNcn85S6jvdk,1790
|
|
60
|
-
tunacode_cli-0.0.
|
|
61
|
-
tunacode_cli-0.0.
|
|
62
|
-
tunacode_cli-0.0.
|
|
63
|
-
tunacode_cli-0.0.
|
|
64
|
-
tunacode_cli-0.0.
|
|
65
|
-
tunacode_cli-0.0.
|
|
68
|
+
tunacode_cli-0.0.6.dist-info/licenses/LICENSE,sha256=SgvEceNP-y3_WodntkMGAkZyl_hAUvzBw5T9W--7YpM,1070
|
|
69
|
+
tunacode_cli-0.0.6.dist-info/METADATA,sha256=7Tqjj-axKmfqKovX66Egt9YttEmEgC9CREPcVb3J1B0,7131
|
|
70
|
+
tunacode_cli-0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
71
|
+
tunacode_cli-0.0.6.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
|
|
72
|
+
tunacode_cli-0.0.6.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
|
|
73
|
+
tunacode_cli-0.0.6.dist-info/RECORD,,
|