tunacode-cli 0.0.13__tar.gz → 0.0.14__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.13 → tunacode_cli-0.0.14}/PKG-INFO +50 -14
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/README.md +50 -14
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/pyproject.toml +1 -1
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/commands.py +205 -14
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/repl.py +41 -2
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/configuration/defaults.py +1 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/constants.py +1 -1
- tunacode_cli-0.0.14/src/tunacode/core/agents/main.py +337 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/state.py +10 -2
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/prompts/system.txt +22 -0
- tunacode_cli-0.0.14/src/tunacode/tools/grep.py +760 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/read_file.py +15 -10
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/run_command.py +13 -7
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/update_file.py +9 -10
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/write_file.py +8 -9
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/PKG-INFO +50 -14
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/SOURCES.txt +1 -0
- tunacode_cli-0.0.13/src/tunacode/core/agents/main.py +0 -121
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/LICENSE +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/setup.cfg +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/main.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/textual_app.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/cli/textual_bridge.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/configuration/models.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/context.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/agent_setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/base.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/config_setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/coordinator.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/environment_setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/setup/git_safety_setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/core/tool_handler.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/exceptions.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/services/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/services/mcp.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/setup.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/base.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/tools/bash.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/types.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/completers.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/console.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/constants.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/decorators.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/input.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/keybindings.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/lexers.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/output.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/panels.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/prompt_manager.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/tool_ui.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/ui/validators.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/__init__.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/bm25.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/diff_utils.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/file_utils.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/ripgrep.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/system.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/text_utils.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode/utils/user_configuration.py +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/requires.txt +0 -0
- {tunacode_cli-0.0.13 → tunacode_cli-0.0.14}/src/tunacode_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunacode-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.14
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -58,13 +58,14 @@ Dynamic: license-file
|
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
61
|
-
### Recent Updates
|
|
61
|
+
### Recent Updates (v0.0.14)
|
|
62
62
|
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
63
|
+
- **🔧 JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
|
|
64
|
+
- **⚡ Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
|
|
65
|
+
- **🔄 Configuration Management**: New `/refresh` command to reload config without restart
|
|
66
|
+
- **🧠 Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
|
|
67
|
+
- **🛠️ New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
|
|
68
|
+
- **📊 Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
|
|
68
69
|
|
|
69
70
|
### Core Features
|
|
70
71
|
|
|
@@ -75,17 +76,18 @@ Dynamic: license-file
|
|
|
75
76
|
### **Multi-Provider Support**
|
|
76
77
|
|
|
77
78
|
- Anthropic Claude
|
|
78
|
-
- OpenAI GPT
|
|
79
|
+
- OpenAI GPT
|
|
79
80
|
- Google Gemini
|
|
80
81
|
- OpenRouter (100+ models)
|
|
81
82
|
- Any OpenAI-compatible API
|
|
82
83
|
|
|
83
84
|
### **Developer Tools**
|
|
84
85
|
|
|
85
|
-
-
|
|
86
|
+
- 6 core tools: bash, grep, read_file, write_file, update_file, run_command
|
|
86
87
|
- MCP (Model Context Protocol) support
|
|
87
88
|
- File operation confirmations with diffs
|
|
88
89
|
- Per-project context guides (TUNACODE.md)
|
|
90
|
+
- JSON tool parsing fallback for API compatibility
|
|
89
91
|
|
|
90
92
|
</td>
|
|
91
93
|
<td width="50%">
|
|
@@ -104,6 +106,7 @@ Dynamic: license-file
|
|
|
104
106
|
- Async throughout
|
|
105
107
|
- Modular command system
|
|
106
108
|
- Rich UI with syntax highlighting
|
|
109
|
+
- ReAct reasoning patterns
|
|
107
110
|
|
|
108
111
|
</td>
|
|
109
112
|
</tr>
|
|
@@ -268,6 +271,8 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
|
|
|
268
271
|
|
|
269
272
|
## Commands Reference
|
|
270
273
|
|
|
274
|
+
### Core Commands
|
|
275
|
+
|
|
271
276
|
| Command | Description |
|
|
272
277
|
| -------------------------------- | -------------------------------- |
|
|
273
278
|
| `/help` | Show available commands |
|
|
@@ -281,13 +286,43 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
|
|
|
281
286
|
| `/dump` | Show message history (debug) |
|
|
282
287
|
| `exit` | Exit application |
|
|
283
288
|
|
|
289
|
+
### Debug & Recovery Commands
|
|
290
|
+
|
|
291
|
+
| Command | Description |
|
|
292
|
+
| -------------------------------- | -------------------------------- |
|
|
293
|
+
| `/thoughts` | Toggle ReAct thought display |
|
|
294
|
+
| `/iterations <1-50>` | Set max reasoning iterations |
|
|
295
|
+
| `/parsetools` | Parse JSON tool calls manually |
|
|
296
|
+
| `/fix` | Fix orphaned tool calls |
|
|
297
|
+
| `/refresh` | Reload configuration from defaults |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Reliability Features
|
|
302
|
+
|
|
303
|
+
### JSON Tool Parsing Fallback
|
|
304
|
+
|
|
305
|
+
TunaCode automatically handles API provider failures with robust JSON parsing:
|
|
306
|
+
|
|
307
|
+
- **Automatic Recovery**: When structured tool calling fails, TunaCode parses JSON from text responses
|
|
308
|
+
- **Multiple Formats**: Supports inline JSON, code blocks, and complex nested structures
|
|
309
|
+
- **Manual Recovery**: Use `/parsetools` when automatic parsing needs assistance
|
|
310
|
+
- **Visual Feedback**: See `🔧 Recovered using JSON tool parsing` messages during fallback
|
|
311
|
+
|
|
312
|
+
### Enhanced Error Handling
|
|
313
|
+
|
|
314
|
+
- **Tool Schema Fixes**: Consistent parameter naming across all tools
|
|
315
|
+
- **Orphaned Tool Call Recovery**: Automatic cleanup with `/fix` command
|
|
316
|
+
- **Configuration Refresh**: Update settings without restart using `/refresh`
|
|
317
|
+
- **ReAct Reasoning**: Configurable iteration limits for complex problem solving
|
|
318
|
+
|
|
284
319
|
---
|
|
285
320
|
|
|
286
321
|
## Customization
|
|
287
322
|
|
|
288
323
|
### Project Guides
|
|
289
324
|
|
|
290
|
-
Create a `TUNACODE.md` file your project root to customize TunaCode's behavior:
|
|
325
|
+
Create a `TUNACODE.md` file in your project root to customize TunaCode's behavior:
|
|
291
326
|
|
|
292
327
|
```markdown
|
|
293
328
|
# Project Guide
|
|
@@ -337,13 +372,14 @@ src/tunacode/
|
|
|
337
372
|
│ └── tool_handler.py # Tool execution and validation
|
|
338
373
|
│
|
|
339
374
|
├── services/ # External Services
|
|
340
|
-
│
|
|
341
|
-
│ └── undo_service.py # Undo operations (beta)
|
|
375
|
+
│ └── mcp.py # Model Context Protocol integration
|
|
342
376
|
│
|
|
343
377
|
├── tools/ # AI Agent Tools
|
|
344
378
|
│ ├── base.py # Tool base classes
|
|
379
|
+
│ ├── bash.py # Enhanced shell command execution
|
|
380
|
+
│ ├── grep.py # Parallel content search tool
|
|
345
381
|
│ ├── read_file.py # File reading tool
|
|
346
|
-
│ ├── run_command.py #
|
|
382
|
+
│ ├── run_command.py # Basic command execution tool
|
|
347
383
|
│ ├── update_file.py # File modification tool
|
|
348
384
|
│ └── write_file.py # File creation tool
|
|
349
385
|
│
|
|
@@ -360,7 +396,7 @@ src/tunacode/
|
|
|
360
396
|
│ └── validators.py # Input validation
|
|
361
397
|
│
|
|
362
398
|
├── utils/ # Utility Functions
|
|
363
|
-
│ ├── bm25.py # BM25 search algorithm(beta)
|
|
399
|
+
│ ├── bm25.py # BM25 search algorithm (beta)
|
|
364
400
|
│ ├── diff_utils.py # Diff generation and formatting
|
|
365
401
|
│ ├── file_utils.py # File system operations
|
|
366
402
|
│ ├── ripgrep.py # Code search utilities
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
### Recent Updates
|
|
25
|
+
### Recent Updates (v0.0.14)
|
|
26
26
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
27
|
+
- **🔧 JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
|
|
28
|
+
- **⚡ Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
|
|
29
|
+
- **🔄 Configuration Management**: New `/refresh` command to reload config without restart
|
|
30
|
+
- **🧠 Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
|
|
31
|
+
- **🛠️ New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
|
|
32
|
+
- **📊 Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
|
|
32
33
|
|
|
33
34
|
### Core Features
|
|
34
35
|
|
|
@@ -39,17 +40,18 @@
|
|
|
39
40
|
### **Multi-Provider Support**
|
|
40
41
|
|
|
41
42
|
- Anthropic Claude
|
|
42
|
-
- OpenAI GPT
|
|
43
|
+
- OpenAI GPT
|
|
43
44
|
- Google Gemini
|
|
44
45
|
- OpenRouter (100+ models)
|
|
45
46
|
- Any OpenAI-compatible API
|
|
46
47
|
|
|
47
48
|
### **Developer Tools**
|
|
48
49
|
|
|
49
|
-
-
|
|
50
|
+
- 6 core tools: bash, grep, read_file, write_file, update_file, run_command
|
|
50
51
|
- MCP (Model Context Protocol) support
|
|
51
52
|
- File operation confirmations with diffs
|
|
52
53
|
- Per-project context guides (TUNACODE.md)
|
|
54
|
+
- JSON tool parsing fallback for API compatibility
|
|
53
55
|
|
|
54
56
|
</td>
|
|
55
57
|
<td width="50%">
|
|
@@ -68,6 +70,7 @@
|
|
|
68
70
|
- Async throughout
|
|
69
71
|
- Modular command system
|
|
70
72
|
- Rich UI with syntax highlighting
|
|
73
|
+
- ReAct reasoning patterns
|
|
71
74
|
|
|
72
75
|
</td>
|
|
73
76
|
</tr>
|
|
@@ -232,6 +235,8 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
|
|
|
232
235
|
|
|
233
236
|
## Commands Reference
|
|
234
237
|
|
|
238
|
+
### Core Commands
|
|
239
|
+
|
|
235
240
|
| Command | Description |
|
|
236
241
|
| -------------------------------- | -------------------------------- |
|
|
237
242
|
| `/help` | Show available commands |
|
|
@@ -245,13 +250,43 @@ Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io/)
|
|
|
245
250
|
| `/dump` | Show message history (debug) |
|
|
246
251
|
| `exit` | Exit application |
|
|
247
252
|
|
|
253
|
+
### Debug & Recovery Commands
|
|
254
|
+
|
|
255
|
+
| Command | Description |
|
|
256
|
+
| -------------------------------- | -------------------------------- |
|
|
257
|
+
| `/thoughts` | Toggle ReAct thought display |
|
|
258
|
+
| `/iterations <1-50>` | Set max reasoning iterations |
|
|
259
|
+
| `/parsetools` | Parse JSON tool calls manually |
|
|
260
|
+
| `/fix` | Fix orphaned tool calls |
|
|
261
|
+
| `/refresh` | Reload configuration from defaults |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Reliability Features
|
|
266
|
+
|
|
267
|
+
### JSON Tool Parsing Fallback
|
|
268
|
+
|
|
269
|
+
TunaCode automatically handles API provider failures with robust JSON parsing:
|
|
270
|
+
|
|
271
|
+
- **Automatic Recovery**: When structured tool calling fails, TunaCode parses JSON from text responses
|
|
272
|
+
- **Multiple Formats**: Supports inline JSON, code blocks, and complex nested structures
|
|
273
|
+
- **Manual Recovery**: Use `/parsetools` when automatic parsing needs assistance
|
|
274
|
+
- **Visual Feedback**: See `🔧 Recovered using JSON tool parsing` messages during fallback
|
|
275
|
+
|
|
276
|
+
### Enhanced Error Handling
|
|
277
|
+
|
|
278
|
+
- **Tool Schema Fixes**: Consistent parameter naming across all tools
|
|
279
|
+
- **Orphaned Tool Call Recovery**: Automatic cleanup with `/fix` command
|
|
280
|
+
- **Configuration Refresh**: Update settings without restart using `/refresh`
|
|
281
|
+
- **ReAct Reasoning**: Configurable iteration limits for complex problem solving
|
|
282
|
+
|
|
248
283
|
---
|
|
249
284
|
|
|
250
285
|
## Customization
|
|
251
286
|
|
|
252
287
|
### Project Guides
|
|
253
288
|
|
|
254
|
-
Create a `TUNACODE.md` file your project root to customize TunaCode's behavior:
|
|
289
|
+
Create a `TUNACODE.md` file in your project root to customize TunaCode's behavior:
|
|
255
290
|
|
|
256
291
|
```markdown
|
|
257
292
|
# Project Guide
|
|
@@ -301,13 +336,14 @@ src/tunacode/
|
|
|
301
336
|
│ └── tool_handler.py # Tool execution and validation
|
|
302
337
|
│
|
|
303
338
|
├── services/ # External Services
|
|
304
|
-
│
|
|
305
|
-
│ └── undo_service.py # Undo operations (beta)
|
|
339
|
+
│ └── mcp.py # Model Context Protocol integration
|
|
306
340
|
│
|
|
307
341
|
├── tools/ # AI Agent Tools
|
|
308
342
|
│ ├── base.py # Tool base classes
|
|
343
|
+
│ ├── bash.py # Enhanced shell command execution
|
|
344
|
+
│ ├── grep.py # Parallel content search tool
|
|
309
345
|
│ ├── read_file.py # File reading tool
|
|
310
|
-
│ ├── run_command.py #
|
|
346
|
+
│ ├── run_command.py # Basic command execution tool
|
|
311
347
|
│ ├── update_file.py # File modification tool
|
|
312
348
|
│ └── write_file.py # File creation tool
|
|
313
349
|
│
|
|
@@ -324,7 +360,7 @@ src/tunacode/
|
|
|
324
360
|
│ └── validators.py # Input validation
|
|
325
361
|
│
|
|
326
362
|
├── utils/ # Utility Functions
|
|
327
|
-
│ ├── bm25.py # BM25 search algorithm(beta)
|
|
363
|
+
│ ├── bm25.py # BM25 search algorithm (beta)
|
|
328
364
|
│ ├── diff_utils.py # Diff generation and formatting
|
|
329
365
|
│ ├── file_utils.py # File system operations
|
|
330
366
|
│ ├── ripgrep.py # Code search utilities
|
|
@@ -402,4 +438,4 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
402
438
|
|
|
403
439
|
## Acknowledgments
|
|
404
440
|
|
|
405
|
-
TunaCode is a fork of [sidekick-cli](https://github.com/geekforbrains/sidekick-cli). Special thanks to the sidekick-cli team for creating the foundation that made TunaCode possible.
|
|
441
|
+
TunaCode is a fork of [sidekick-cli](https://github.com/geekforbrains/sidekick-cli). Special thanks to the sidekick-cli team for creating the foundation that made TunaCode possible.
|
|
@@ -138,6 +138,73 @@ class DumpCommand(SimpleCommand):
|
|
|
138
138
|
await ui.dump_messages(context.state_manager.session.messages)
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
class ThoughtsCommand(SimpleCommand):
|
|
142
|
+
"""Toggle display of agent thoughts."""
|
|
143
|
+
|
|
144
|
+
def __init__(self):
|
|
145
|
+
super().__init__(
|
|
146
|
+
CommandSpec(
|
|
147
|
+
name="thoughts",
|
|
148
|
+
aliases=["/thoughts"],
|
|
149
|
+
description="Show or hide agent thought messages",
|
|
150
|
+
category=CommandCategory.DEBUG,
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
155
|
+
state = context.state_manager.session
|
|
156
|
+
if args:
|
|
157
|
+
arg = args[0].lower()
|
|
158
|
+
if arg in {"on", "1", "true"}:
|
|
159
|
+
state.show_thoughts = True
|
|
160
|
+
elif arg in {"off", "0", "false"}:
|
|
161
|
+
state.show_thoughts = False
|
|
162
|
+
else:
|
|
163
|
+
await ui.error("Usage: /thoughts [on|off]")
|
|
164
|
+
return
|
|
165
|
+
else:
|
|
166
|
+
state.show_thoughts = not state.show_thoughts
|
|
167
|
+
status = "ON" if state.show_thoughts else "OFF"
|
|
168
|
+
await ui.success(f"Thought display {status}")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class IterationsCommand(SimpleCommand):
|
|
172
|
+
"""Configure maximum agent iterations for ReAct reasoning."""
|
|
173
|
+
|
|
174
|
+
def __init__(self):
|
|
175
|
+
super().__init__(
|
|
176
|
+
CommandSpec(
|
|
177
|
+
name="iterations",
|
|
178
|
+
aliases=["/iterations"],
|
|
179
|
+
description="Set maximum agent iterations for complex reasoning",
|
|
180
|
+
category=CommandCategory.DEBUG,
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
185
|
+
state = context.state_manager.session
|
|
186
|
+
if args:
|
|
187
|
+
try:
|
|
188
|
+
new_limit = int(args[0])
|
|
189
|
+
if new_limit < 1 or new_limit > 50:
|
|
190
|
+
await ui.error("Iterations must be between 1 and 50")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
# Update the user config
|
|
194
|
+
if "settings" not in state.user_config:
|
|
195
|
+
state.user_config["settings"] = {}
|
|
196
|
+
state.user_config["settings"]["max_iterations"] = new_limit
|
|
197
|
+
|
|
198
|
+
await ui.success(f"Maximum iterations set to {new_limit}")
|
|
199
|
+
await ui.muted("Higher values allow more complex reasoning but may be slower")
|
|
200
|
+
except ValueError:
|
|
201
|
+
await ui.error("Please provide a valid number")
|
|
202
|
+
else:
|
|
203
|
+
current = state.user_config.get("settings", {}).get("max_iterations", 15)
|
|
204
|
+
await ui.info(f"Current maximum iterations: {current}")
|
|
205
|
+
await ui.muted("Usage: /iterations <number> (1-50)")
|
|
206
|
+
|
|
207
|
+
|
|
141
208
|
class ClearCommand(SimpleCommand):
|
|
142
209
|
"""Clear screen and message history."""
|
|
143
210
|
|
|
@@ -152,8 +219,127 @@ class ClearCommand(SimpleCommand):
|
|
|
152
219
|
)
|
|
153
220
|
|
|
154
221
|
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
222
|
+
# Patch any orphaned tool calls before clearing
|
|
223
|
+
from tunacode.core.agents.main import patch_tool_messages
|
|
224
|
+
patch_tool_messages("Conversation cleared", context.state_manager)
|
|
225
|
+
|
|
155
226
|
await ui.clear()
|
|
156
227
|
context.state_manager.session.messages = []
|
|
228
|
+
await ui.success("Message history cleared")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class FixCommand(SimpleCommand):
|
|
232
|
+
"""Fix orphaned tool calls that cause API errors."""
|
|
233
|
+
|
|
234
|
+
def __init__(self):
|
|
235
|
+
super().__init__(
|
|
236
|
+
CommandSpec(
|
|
237
|
+
name="fix",
|
|
238
|
+
aliases=["/fix"],
|
|
239
|
+
description="Fix orphaned tool calls causing API errors",
|
|
240
|
+
category=CommandCategory.DEBUG,
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
245
|
+
from tunacode.core.agents.main import patch_tool_messages
|
|
246
|
+
|
|
247
|
+
# Count current messages
|
|
248
|
+
before_count = len(context.state_manager.session.messages)
|
|
249
|
+
|
|
250
|
+
# Patch orphaned tool calls
|
|
251
|
+
patch_tool_messages("Tool call resolved by /fix command", context.state_manager)
|
|
252
|
+
|
|
253
|
+
# Count after patching
|
|
254
|
+
after_count = len(context.state_manager.session.messages)
|
|
255
|
+
patched_count = after_count - before_count
|
|
256
|
+
|
|
257
|
+
if patched_count > 0:
|
|
258
|
+
await ui.success(f"Fixed {patched_count} orphaned tool call(s)")
|
|
259
|
+
await ui.muted("You can now continue the conversation normally")
|
|
260
|
+
else:
|
|
261
|
+
await ui.info("No orphaned tool calls found")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class ParseToolsCommand(SimpleCommand):
|
|
265
|
+
"""Parse and execute JSON tool calls from the last response."""
|
|
266
|
+
|
|
267
|
+
def __init__(self):
|
|
268
|
+
super().__init__(
|
|
269
|
+
CommandSpec(
|
|
270
|
+
name="parsetools",
|
|
271
|
+
aliases=["/parsetools"],
|
|
272
|
+
description="Parse JSON tool calls from last response when structured calling fails",
|
|
273
|
+
category=CommandCategory.DEBUG,
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
278
|
+
from tunacode.core.agents.main import extract_and_execute_tool_calls
|
|
279
|
+
|
|
280
|
+
# Find the last model response in messages
|
|
281
|
+
messages = context.state_manager.session.messages
|
|
282
|
+
if not messages:
|
|
283
|
+
await ui.error("No message history found")
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
# Look for the most recent response with text content
|
|
287
|
+
found_content = False
|
|
288
|
+
for msg in reversed(messages):
|
|
289
|
+
if hasattr(msg, 'parts'):
|
|
290
|
+
for part in msg.parts:
|
|
291
|
+
if hasattr(part, 'content') and isinstance(part.content, str):
|
|
292
|
+
# Create tool callback
|
|
293
|
+
from tunacode.cli.repl import _tool_handler
|
|
294
|
+
def tool_callback_with_state(part, node):
|
|
295
|
+
return _tool_handler(part, node, context.state_manager)
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
await extract_and_execute_tool_calls(
|
|
299
|
+
part.content,
|
|
300
|
+
tool_callback_with_state,
|
|
301
|
+
context.state_manager
|
|
302
|
+
)
|
|
303
|
+
await ui.success("JSON tool parsing completed")
|
|
304
|
+
found_content = True
|
|
305
|
+
return
|
|
306
|
+
except Exception as e:
|
|
307
|
+
await ui.error(f"Failed to parse tools: {str(e)}")
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
if not found_content:
|
|
311
|
+
await ui.error("No parseable content found in recent messages")
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class RefreshConfigCommand(SimpleCommand):
|
|
315
|
+
"""Refresh configuration from defaults."""
|
|
316
|
+
|
|
317
|
+
def __init__(self):
|
|
318
|
+
super().__init__(
|
|
319
|
+
CommandSpec(
|
|
320
|
+
name="refresh",
|
|
321
|
+
aliases=["/refresh"],
|
|
322
|
+
description="Refresh configuration from defaults (useful after updates)",
|
|
323
|
+
category=CommandCategory.SYSTEM,
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
328
|
+
from tunacode.configuration.defaults import DEFAULT_USER_CONFIG
|
|
329
|
+
|
|
330
|
+
# Update current session config with latest defaults
|
|
331
|
+
for key, value in DEFAULT_USER_CONFIG.items():
|
|
332
|
+
if key not in context.state_manager.session.user_config:
|
|
333
|
+
context.state_manager.session.user_config[key] = value
|
|
334
|
+
elif isinstance(value, dict):
|
|
335
|
+
# Merge dict values, preserving user overrides
|
|
336
|
+
for subkey, subvalue in value.items():
|
|
337
|
+
if subkey not in context.state_manager.session.user_config[key]:
|
|
338
|
+
context.state_manager.session.user_config[key][subkey] = subvalue
|
|
339
|
+
|
|
340
|
+
# Show updated max_iterations
|
|
341
|
+
max_iterations = context.state_manager.session.user_config.get("settings", {}).get("max_iterations", 20)
|
|
342
|
+
await ui.success(f"Configuration refreshed - max iterations: {max_iterations}")
|
|
157
343
|
|
|
158
344
|
|
|
159
345
|
class TunaCodeCommand(SimpleCommand):
|
|
@@ -232,7 +418,6 @@ class HelpCommand(SimpleCommand):
|
|
|
232
418
|
await ui.help(self._command_registry)
|
|
233
419
|
|
|
234
420
|
|
|
235
|
-
|
|
236
421
|
class BranchCommand(SimpleCommand):
|
|
237
422
|
"""Create and switch to a new git branch."""
|
|
238
423
|
|
|
@@ -247,8 +432,8 @@ class BranchCommand(SimpleCommand):
|
|
|
247
432
|
)
|
|
248
433
|
|
|
249
434
|
async def execute(self, args: List[str], context: CommandContext) -> None:
|
|
250
|
-
import subprocess
|
|
251
435
|
import os
|
|
436
|
+
import subprocess
|
|
252
437
|
|
|
253
438
|
if not args:
|
|
254
439
|
await ui.error("Usage: /branch <branch-name>")
|
|
@@ -332,14 +517,16 @@ class ModelCommand(SimpleCommand):
|
|
|
332
517
|
|
|
333
518
|
# Get the model name from args
|
|
334
519
|
model_name = args[0]
|
|
335
|
-
|
|
520
|
+
|
|
336
521
|
# Check if provider prefix is present
|
|
337
522
|
if ":" not in model_name:
|
|
338
523
|
await ui.error("Model name must include provider prefix")
|
|
339
524
|
await ui.muted("Format: provider:model-name")
|
|
340
|
-
await ui.muted(
|
|
525
|
+
await ui.muted(
|
|
526
|
+
"Examples: openai:gpt-4.1, anthropic:claude-3-opus, google-gla:gemini-2.0-flash"
|
|
527
|
+
)
|
|
341
528
|
return None
|
|
342
|
-
|
|
529
|
+
|
|
343
530
|
# No validation - user is responsible for correct model names
|
|
344
531
|
await ui.warning("Model set without validation - verify the model name is correct")
|
|
345
532
|
|
|
@@ -416,8 +603,7 @@ class CommandRegistry:
|
|
|
416
603
|
category_commands = self._categories[command.category]
|
|
417
604
|
# Remove any existing instance of this command class
|
|
418
605
|
self._categories[command.category] = [
|
|
419
|
-
cmd for cmd in category_commands
|
|
420
|
-
if cmd.__class__ != command.__class__
|
|
606
|
+
cmd for cmd in category_commands if cmd.__class__ != command.__class__
|
|
421
607
|
]
|
|
422
608
|
# Add the new instance
|
|
423
609
|
self._categories[command.category].append(command)
|
|
@@ -436,7 +622,12 @@ class CommandRegistry:
|
|
|
436
622
|
command_classes = [
|
|
437
623
|
YoloCommand,
|
|
438
624
|
DumpCommand,
|
|
625
|
+
ThoughtsCommand,
|
|
626
|
+
IterationsCommand,
|
|
439
627
|
ClearCommand,
|
|
628
|
+
FixCommand,
|
|
629
|
+
ParseToolsCommand,
|
|
630
|
+
RefreshConfigCommand,
|
|
440
631
|
HelpCommand,
|
|
441
632
|
BranchCommand,
|
|
442
633
|
# TunaCodeCommand, # TODO: Temporarily disabled
|
|
@@ -459,7 +650,7 @@ class CommandRegistry:
|
|
|
459
650
|
# Only update if callback has changed
|
|
460
651
|
if self._factory.dependencies.process_request_callback == callback:
|
|
461
652
|
return
|
|
462
|
-
|
|
653
|
+
|
|
463
654
|
self._factory.update_dependencies(process_request_callback=callback)
|
|
464
655
|
|
|
465
656
|
# Re-register CompactCommand with new dependency if already registered
|
|
@@ -494,10 +685,10 @@ class CommandRegistry:
|
|
|
494
685
|
if command_name in self._commands:
|
|
495
686
|
command = self._commands[command_name]
|
|
496
687
|
return await command.execute(args, context)
|
|
497
|
-
|
|
688
|
+
|
|
498
689
|
# Try partial matching
|
|
499
690
|
matches = self.find_matching_commands(command_name)
|
|
500
|
-
|
|
691
|
+
|
|
501
692
|
if not matches:
|
|
502
693
|
raise ValidationError(f"Unknown command: {command_name}")
|
|
503
694
|
elif len(matches) == 1:
|
|
@@ -513,10 +704,10 @@ class CommandRegistry:
|
|
|
513
704
|
def find_matching_commands(self, partial_command: str) -> List[str]:
|
|
514
705
|
"""
|
|
515
706
|
Find all commands that start with the given partial command.
|
|
516
|
-
|
|
707
|
+
|
|
517
708
|
Args:
|
|
518
709
|
partial_command: The partial command to match
|
|
519
|
-
|
|
710
|
+
|
|
520
711
|
Returns:
|
|
521
712
|
List of matching command names
|
|
522
713
|
"""
|
|
@@ -534,11 +725,11 @@ class CommandRegistry:
|
|
|
534
725
|
return False
|
|
535
726
|
|
|
536
727
|
command_name = parts[0].lower()
|
|
537
|
-
|
|
728
|
+
|
|
538
729
|
# Check exact match first
|
|
539
730
|
if command_name in self._commands:
|
|
540
731
|
return True
|
|
541
|
-
|
|
732
|
+
|
|
542
733
|
# Check partial match
|
|
543
734
|
return len(self.find_matching_commands(command_name)) > 0
|
|
544
735
|
|
|
@@ -167,10 +167,14 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
167
167
|
await ui.error(str(e))
|
|
168
168
|
return
|
|
169
169
|
|
|
170
|
+
# Patch any orphaned tool calls from previous requests before proceeding
|
|
171
|
+
patch_tool_messages("Tool execution was interrupted", state_manager)
|
|
172
|
+
|
|
170
173
|
# Create a partial function that includes state_manager
|
|
171
174
|
def tool_callback_with_state(part, node):
|
|
172
175
|
return _tool_handler(part, node, state_manager)
|
|
173
176
|
|
|
177
|
+
start_idx = len(state_manager.session.messages)
|
|
174
178
|
res = await agent.process_request(
|
|
175
179
|
state_manager.session.current_model,
|
|
176
180
|
text,
|
|
@@ -178,7 +182,17 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
178
182
|
tool_callback=tool_callback_with_state,
|
|
179
183
|
)
|
|
180
184
|
if output:
|
|
181
|
-
|
|
185
|
+
if state_manager.session.show_thoughts:
|
|
186
|
+
new_msgs = state_manager.session.messages[start_idx:]
|
|
187
|
+
for msg in new_msgs:
|
|
188
|
+
if isinstance(msg, dict) and "thought" in msg:
|
|
189
|
+
await ui.muted(f"THOUGHT: {msg['thought']}")
|
|
190
|
+
# Check if result exists and has output
|
|
191
|
+
if hasattr(res, 'result') and res.result is not None and hasattr(res.result, 'output'):
|
|
192
|
+
await ui.agent(res.result.output)
|
|
193
|
+
else:
|
|
194
|
+
# Fallback: show that the request was processed
|
|
195
|
+
await ui.muted("Request completed")
|
|
182
196
|
except CancelledError:
|
|
183
197
|
await ui.muted("Request cancelled")
|
|
184
198
|
except UserAbortError:
|
|
@@ -188,6 +202,31 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
188
202
|
await ui.muted(error_message)
|
|
189
203
|
patch_tool_messages(error_message, state_manager)
|
|
190
204
|
except Exception as e:
|
|
205
|
+
# Check if this might be a tool calling failure that we can recover from
|
|
206
|
+
error_str = str(e).lower()
|
|
207
|
+
if any(keyword in error_str for keyword in ['tool', 'function', 'call', 'schema']):
|
|
208
|
+
# Try to extract and execute tool calls from the last response
|
|
209
|
+
if state_manager.session.messages:
|
|
210
|
+
last_msg = state_manager.session.messages[-1]
|
|
211
|
+
if hasattr(last_msg, 'parts'):
|
|
212
|
+
for part in last_msg.parts:
|
|
213
|
+
if hasattr(part, 'content') and isinstance(part.content, str):
|
|
214
|
+
from tunacode.core.agents.main import extract_and_execute_tool_calls
|
|
215
|
+
try:
|
|
216
|
+
# Create a partial function that includes state_manager
|
|
217
|
+
def tool_callback_with_state(part, node):
|
|
218
|
+
return _tool_handler(part, node, state_manager)
|
|
219
|
+
|
|
220
|
+
await extract_and_execute_tool_calls(
|
|
221
|
+
part.content,
|
|
222
|
+
tool_callback_with_state,
|
|
223
|
+
state_manager
|
|
224
|
+
)
|
|
225
|
+
await ui.warning("🔧 Recovered using JSON tool parsing")
|
|
226
|
+
return # Successfully recovered
|
|
227
|
+
except Exception:
|
|
228
|
+
pass # Fallback failed, continue with normal error handling
|
|
229
|
+
|
|
191
230
|
# Wrap unexpected exceptions in AgentError for better tracking
|
|
192
231
|
agent_error = AgentError(f"Agent processing failed: {str(e)}")
|
|
193
232
|
agent_error.__cause__ = e # Preserve the original exception chain
|
|
@@ -210,7 +249,7 @@ async def repl(state_manager: StateManager):
|
|
|
210
249
|
await ui.muted(f"• Model: {state_manager.session.current_model}")
|
|
211
250
|
await ui.success("Ready to assist with your development")
|
|
212
251
|
await ui.line()
|
|
213
|
-
|
|
252
|
+
|
|
214
253
|
instance = agent.get_or_create_agent(state_manager.session.current_model, state_manager)
|
|
215
254
|
|
|
216
255
|
async with instance.run_mcp_servers():
|