tunacode-cli 0.0.22__tar.gz → 0.0.23__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.

Files changed (98) hide show
  1. tunacode_cli-0.0.23/CLAUDE.md +120 -0
  2. tunacode_cli-0.0.23/MANIFEST.in +19 -0
  3. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/PKG-INFO +1 -1
  4. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/pyproject.toml +1 -1
  5. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/setup.py +1 -1
  6. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/commands.py +1 -1
  7. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/main.py +1 -0
  8. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/repl.py +14 -6
  9. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/textual_app.py +19 -25
  10. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/textual_bridge.py +2 -3
  11. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/constants.py +1 -1
  12. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/context.py +0 -2
  13. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/agents/main.py +21 -18
  14. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/llm/planner.py +0 -1
  15. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/config_setup.py +6 -2
  16. tunacode_cli-0.0.23/src/tunacode/prompts/system.md +92 -0
  17. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/__init__.py +1 -1
  18. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/grep.py +2 -2
  19. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/types.py +4 -0
  20. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/completers.py +5 -4
  21. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/input.py +1 -2
  22. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/lexers.py +0 -1
  23. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/output.py +2 -2
  24. tunacode_cli-0.0.23/src/tunacode/utils/__init__.py +0 -0
  25. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/PKG-INFO +1 -1
  26. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/SOURCES.txt +6 -0
  27. tunacode_cli-0.0.23/tests/test_agent_initialization.py +147 -0
  28. tunacode_cli-0.0.23/tests/test_config_setup_async.py +85 -0
  29. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/LICENSE +0 -0
  30. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/README.md +0 -0
  31. /tunacode_cli-0.0.22/src/tunacode/__init__.py → /tunacode_cli-0.0.23/TUNACODE.md +0 -0
  32. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/setup.cfg +0 -0
  33. {tunacode_cli-0.0.22/src/tunacode/core → tunacode_cli-0.0.23/src/tunacode}/__init__.py +0 -0
  34. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/cli/__init__.py +0 -0
  35. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/configuration/__init__.py +0 -0
  36. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/configuration/defaults.py +0 -0
  37. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/configuration/models.py +0 -0
  38. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/configuration/settings.py +0 -0
  39. {tunacode_cli-0.0.22/src/tunacode/core/background → tunacode_cli-0.0.23/src/tunacode/core}/__init__.py +0 -0
  40. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/agents/__init__.py +0 -0
  41. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/agents/orchestrator.py +0 -0
  42. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/agents/planner_schema.py +0 -0
  43. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/agents/readonly.py +0 -0
  44. {tunacode_cli-0.0.22/src/tunacode/core/llm → tunacode_cli-0.0.23/src/tunacode/core/background}/__init__.py +0 -0
  45. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/background/manager.py +0 -0
  46. {tunacode_cli-0.0.22/src/tunacode/utils → tunacode_cli-0.0.23/src/tunacode/core/llm}/__init__.py +0 -0
  47. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/__init__.py +0 -0
  48. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/agent_setup.py +0 -0
  49. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/base.py +0 -0
  50. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/coordinator.py +0 -0
  51. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/environment_setup.py +0 -0
  52. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/setup/git_safety_setup.py +0 -0
  53. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/state.py +0 -0
  54. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/core/tool_handler.py +0 -0
  55. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/exceptions.py +0 -0
  56. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/prompts/system.txt +0 -0
  57. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/py.typed +0 -0
  58. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/services/__init__.py +0 -0
  59. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/services/mcp.py +0 -0
  60. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/setup.py +0 -0
  61. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/base.py +0 -0
  62. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/bash.py +0 -0
  63. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/read_file.py +0 -0
  64. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/run_command.py +0 -0
  65. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/update_file.py +0 -0
  66. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/tools/write_file.py +0 -0
  67. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/__init__.py +0 -0
  68. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/console.py +0 -0
  69. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/constants.py +0 -0
  70. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/decorators.py +0 -0
  71. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/keybindings.py +0 -0
  72. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/panels.py +0 -0
  73. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/prompt_manager.py +0 -0
  74. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/tool_ui.py +0 -0
  75. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/ui/validators.py +0 -0
  76. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/bm25.py +0 -0
  77. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/diff_utils.py +0 -0
  78. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/file_utils.py +0 -0
  79. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/import_cache.py +0 -0
  80. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/ripgrep.py +0 -0
  81. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/system.py +0 -0
  82. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/text_utils.py +0 -0
  83. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode/utils/user_configuration.py +0 -0
  84. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
  85. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
  86. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/requires.txt +0 -0
  87. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/src/tunacode_cli.egg-info/top_level.txt +0 -0
  88. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_architect_integration.py +0 -0
  89. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_architect_simple.py +0 -0
  90. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_background_manager.py +0 -0
  91. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_fast_glob_search.py +0 -0
  92. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_file_reference_expansion.py +0 -0
  93. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_json_tool_parsing.py +0 -0
  94. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_orchestrator_file_references.py +0 -0
  95. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_orchestrator_import.py +0 -0
  96. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_orchestrator_planning_visibility.py +0 -0
  97. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/tests/test_react_thoughts.py +0 -0
  98. {tunacode_cli-0.0.22 → tunacode_cli-0.0.23}/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.22
3
+ Version: 0.0.23
4
4
  Summary: Your agentic CLI developer.
5
5
  Author-email: larock22 <noreply@github.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tunacode-cli"
7
- version = "0.0.22"
7
+ version = "0.0.23"
8
8
  description = "Your agentic CLI developer."
9
9
  keywords = ["cli", "agent", "development", "automation"]
10
10
  readme = "README.md"
@@ -5,6 +5,6 @@ setup(
5
5
  packages=find_namespace_packages(where="src"),
6
6
  include_package_data=True,
7
7
  package_data={
8
- "tunacode": ["prompts/*.txt"],
8
+ "tunacode": ["prompts/*.txt", "prompts/*.md"],
9
9
  },
10
10
  )
@@ -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, 'architect_mode', False)
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")
@@ -51,6 +51,7 @@ def main(
51
51
  await repl(state_manager)
52
52
  except Exception as e:
53
53
  from tunacode.exceptions import ConfigurationError
54
+
54
55
  if isinstance(e, ConfigurationError):
55
56
  # ConfigurationError already printed helpful message, just exit cleanly
56
57
  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, 'architect_mode', False):
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 hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
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 hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
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
@@ -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 rich.markdown import Markdown
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.reactive import reactive
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\nType '/help' for available commands",
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, Optional
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
- request = tool_handler.create_confirmation_request(part.tool_name, args)
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,7 +7,7 @@ Centralizes all magic strings, UI text, error messages, and application constant
7
7
 
8
8
  # Application info
9
9
  APP_NAME = "TunaCode"
10
- APP_VERSION = "0.0.22"
10
+ APP_VERSION = "0.0.23"
11
11
 
12
12
  # File patterns
13
13
  GUIDE_FILE_PATTERN = "{name}.md"
@@ -1,5 +1,3 @@
1
- import json
2
- import os
3
1
  import subprocess
4
2
  from pathlib import Path
5
3
  from typing import Dict, List
@@ -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
- # Lazy import for Agent and Tool
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["settings"]["max_retries"]
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
- system_prompt = None
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,
@@ -1,4 +1,3 @@
1
- import json
2
1
  from typing import List
3
2
 
4
3
  from ...types import ModelName
@@ -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' --key 'your-key' --baseurl 'https://openrouter.ai/api/v1'[/green]"
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
- raise ConfigurationError("No configuration found. Please use CLI flags to configure.")
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(
@@ -0,0 +1,92 @@
1
+ \###Instruction###
2
+
3
+ You are **"TunaCode"**, a **senior software developer AI assistant operating inside the user's terminal (CLI)**.
4
+
5
+ **YOU ARE NOT A CHATBOT. YOU ARE AN OPERATIONAL AGENT WITH TOOLS.**
6
+
7
+ Your task is to **execute real actions** via tools and **report observations** after every tool use.
8
+
9
+ You MUST follow these rules:
10
+
11
+ ---
12
+
13
+ \###Tool Access Rules###
14
+
15
+ You HAVE the following tools available. USE THEM IMMEDIATELY and CONSTANTLY:
16
+
17
+ * `run_command(command: str)` — Execute any shell command
18
+ * `read_file(filepath: str)` — Read any file
19
+ * `write_file(filepath: str, content: str)` — Create or write any file
20
+ * `update_file(filepath: str, target: str, patch: str)` — Update existing files
21
+
22
+ ---
23
+
24
+ \###Mandatory Operating Principles###
25
+
26
+ 1. **TOOLS FIRST, ALWAYS**: Start every response with tool usage—**no assumptions**.
27
+ 2. **USE REAL PATHS**: Files live in `/cli/`, `/core/`, `/tools/`, `/services/`, `/configuration/`, `/utils/`, or `/ui/`.
28
+ 3. **CHAIN TOOLS**: First explore (`run_command`), then read (`read_file`), then modify (`update_file`, `write_file`).
29
+ 4. **ACT IMMEDIATELY**: Don’t describe what to do—**just do it**.
30
+ 5. **NO GUESSING**: Verify file existence with `run_command("ls path/")` before reading or writing.
31
+ 6. **ASSUME NOTHING**: Always fetch and verify before responding.
32
+
33
+ ---
34
+
35
+ \###Prompt Design Style###
36
+
37
+ * Be **blunt and direct**. Avoid soft language (e.g., “please,” “let me,” “I think”).
38
+ * **Use role-specific language**: you are a CLI-level senior engineer, not a tutor or assistant.
39
+ * Write using affirmative imperatives: *Do this. Check that. Show me.*
40
+ * Ask for clarification if needed: “Specify the path.” / “Which class do you mean?”
41
+ * Break complex requests into sequenced tool actions.
42
+
43
+ ---
44
+
45
+ \###Example Prompts (Correct vs Incorrect)###
46
+
47
+ **User**: What's in the tools directory?
48
+ ✅ `run_command("ls -la tools/")`
49
+ ❌ "The tools directory likely includes..."
50
+
51
+ **User**: Fix the import in `core/agents/main.py`
52
+ ✅ `read_file("core/agents/main.py")`, then `update_file("core/agents/main.py", "from old_module", "from new_module")`
53
+ ❌ "To fix the import, modify the code to..."
54
+
55
+ **User**: What commands are available?
56
+ ✅ `run_command("grep -E 'class.*Command' cli/commands.py")`
57
+ ❌ "Available commands usually include..."
58
+
59
+ ---
60
+
61
+ \###Meta Behavior###
62
+
63
+ Use the **ReAct** (Reasoning + Action) framework:
64
+
65
+ * {"thought": "I need to inspect the file before modifying."}
66
+ * → run tool
67
+ * {"thought": "I see the old import. Now I'll patch it."}
68
+ * → update file
69
+ * {"thought": "Patch complete. Ready for next instruction."}
70
+
71
+ ---
72
+
73
+ \###Reminder###
74
+
75
+ You were created by **tunahorse21**.
76
+ You are not a chatbot.
77
+ You are an autonomous code execution agent.
78
+ You will be penalized for failing to use tools.
79
+ ---
80
+
81
+ \###Example###
82
+
83
+ ```plaintext
84
+ User: What’s the current app version?
85
+
86
+ THINK: {"thought": "I should search for APP_VERSION in the constants file."}
87
+ ACT: run_command("grep -n 'APP_VERSION' constants.py")
88
+ OBSERVE: {"thought": "Found APP_VERSION at line 12."}
89
+ ACT: read_file("constants.py")
90
+ OBSERVE: {"thought": "APP_VERSION is set to '2.4.1'. This is the current version."}
91
+ RESPONSE: "Current version is 2.4.1 (from constants.py)"
92
+ ```
@@ -7,4 +7,4 @@ def __getattr__(name):
7
7
  try:
8
8
  return importlib.import_module(f".{name}", __name__)
9
9
  except ImportError as e:
10
- raise AttributeError(f"module {{__name__}} has no attribute '{{name}}'") from e
10
+ raise AttributeError(f"module {__name__} has no attribute '{name}'") from e
@@ -16,7 +16,7 @@ import subprocess
16
16
  from concurrent.futures import ThreadPoolExecutor
17
17
  from dataclasses import dataclass
18
18
  from pathlib import Path
19
- from typing import Dict, List, Optional, Tuple, Union
19
+ from typing import List, Optional
20
20
 
21
21
  from tunacode.exceptions import ToolExecutionError
22
22
  from tunacode.tools.base import BaseTool
@@ -239,7 +239,7 @@ class ParallelGrep(BaseTool):
239
239
  results = await self._ripgrep_search(pattern, directory, config)
240
240
  if results:
241
241
  return results
242
- except:
242
+ except Exception:
243
243
  pass
244
244
 
245
245
  # Fallback to Python implementation
@@ -107,9 +107,13 @@ class UILogger(Protocol):
107
107
  """Protocol for UI logging operations."""
108
108
 
109
109
  async def info(self, message: str) -> None: ...
110
+
110
111
  async def error(self, message: str) -> None: ...
112
+
111
113
  async def warning(self, message: str) -> None: ...
114
+
112
115
  async def debug(self, message: str) -> None: ...
116
+
113
117
  async def success(self, message: str) -> None: ...
114
118
 
115
119