tunacode-cli 0.0.18__tar.gz → 0.0.21__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 (93) hide show
  1. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/PKG-INFO +29 -13
  2. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/README.md +28 -12
  3. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/pyproject.toml +1 -1
  4. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/commands.py +34 -0
  5. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/main.py +3 -1
  6. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/repl.py +57 -28
  7. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/constants.py +1 -1
  8. tunacode_cli-0.0.21/src/tunacode/core/agents/__init__.py +12 -0
  9. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/agents/main.py +1 -1
  10. tunacode_cli-0.0.21/src/tunacode/core/agents/orchestrator.py +99 -0
  11. tunacode_cli-0.0.21/src/tunacode/core/agents/planner_schema.py +9 -0
  12. tunacode_cli-0.0.21/src/tunacode/core/agents/readonly.py +51 -0
  13. tunacode_cli-0.0.21/src/tunacode/core/background/manager.py +36 -0
  14. tunacode_cli-0.0.21/src/tunacode/core/llm/planner.py +63 -0
  15. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/config_setup.py +22 -2
  16. tunacode_cli-0.0.21/src/tunacode/prompts/system.txt +203 -0
  17. tunacode_cli-0.0.21/src/tunacode/utils/__init__.py +0 -0
  18. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/PKG-INFO +29 -13
  19. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/SOURCES.txt +14 -0
  20. tunacode_cli-0.0.21/tests/test_architect_integration.py +120 -0
  21. tunacode_cli-0.0.21/tests/test_architect_simple.py +125 -0
  22. tunacode_cli-0.0.21/tests/test_background_manager.py +26 -0
  23. tunacode_cli-0.0.21/tests/test_file_reference_expansion.py +158 -0
  24. tunacode_cli-0.0.21/tests/test_orchestrator_file_references.py +146 -0
  25. tunacode_cli-0.0.21/tests/test_orchestrator_import.py +22 -0
  26. tunacode_cli-0.0.21/tests/test_orchestrator_planning_visibility.py +56 -0
  27. tunacode_cli-0.0.18/src/tunacode/prompts/system.txt +0 -93
  28. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/LICENSE +0 -0
  29. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/setup.cfg +0 -0
  30. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/setup.py +0 -0
  31. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/__init__.py +0 -0
  32. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/__init__.py +0 -0
  33. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/textual_app.py +0 -0
  34. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/cli/textual_bridge.py +0 -0
  35. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/configuration/__init__.py +0 -0
  36. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/configuration/defaults.py +0 -0
  37. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/configuration/models.py +0 -0
  38. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/configuration/settings.py +0 -0
  39. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/context.py +0 -0
  40. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/__init__.py +0 -0
  41. {tunacode_cli-0.0.18/src/tunacode/core/agents → tunacode_cli-0.0.21/src/tunacode/core/background}/__init__.py +0 -0
  42. {tunacode_cli-0.0.18/src/tunacode/utils → tunacode_cli-0.0.21/src/tunacode/core/llm}/__init__.py +0 -0
  43. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/__init__.py +0 -0
  44. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/agent_setup.py +0 -0
  45. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/base.py +0 -0
  46. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/coordinator.py +0 -0
  47. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/environment_setup.py +0 -0
  48. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/setup/git_safety_setup.py +0 -0
  49. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/state.py +0 -0
  50. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/core/tool_handler.py +0 -0
  51. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/exceptions.py +0 -0
  52. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/py.typed +0 -0
  53. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/services/__init__.py +0 -0
  54. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/services/mcp.py +0 -0
  55. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/setup.py +0 -0
  56. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/__init__.py +0 -0
  57. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/base.py +0 -0
  58. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/bash.py +0 -0
  59. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/grep.py +0 -0
  60. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/read_file.py +0 -0
  61. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/run_command.py +0 -0
  62. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/update_file.py +0 -0
  63. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/tools/write_file.py +0 -0
  64. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/types.py +0 -0
  65. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/__init__.py +0 -0
  66. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/completers.py +0 -0
  67. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/console.py +0 -0
  68. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/constants.py +0 -0
  69. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/decorators.py +0 -0
  70. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/input.py +0 -0
  71. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/keybindings.py +0 -0
  72. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/lexers.py +0 -0
  73. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/output.py +0 -0
  74. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/panels.py +0 -0
  75. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/prompt_manager.py +0 -0
  76. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/tool_ui.py +0 -0
  77. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/ui/validators.py +0 -0
  78. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/bm25.py +0 -0
  79. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/diff_utils.py +0 -0
  80. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/file_utils.py +0 -0
  81. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/import_cache.py +0 -0
  82. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/ripgrep.py +0 -0
  83. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/system.py +0 -0
  84. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/text_utils.py +0 -0
  85. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode/utils/user_configuration.py +0 -0
  86. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
  87. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
  88. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/requires.txt +0 -0
  89. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/src/tunacode_cli.egg-info/top_level.txt +0 -0
  90. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/tests/test_fast_glob_search.py +0 -0
  91. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/tests/test_json_tool_parsing.py +0 -0
  92. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/tests/test_react_thoughts.py +0 -0
  93. {tunacode_cli-0.0.18 → tunacode_cli-0.0.21}/tests/test_update_command.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.0.18
3
+ Version: 0.0.21
4
4
  Summary: Your agentic CLI developer.
5
5
  Author-email: larock22 <noreply@github.com>
6
6
  License-Expression: MIT
@@ -58,16 +58,20 @@ Dynamic: license-file
58
58
 
59
59
  ---
60
60
 
61
- ### Recent Updates (v0.0.15)
62
-
63
- - **🐚 Shell Command Support**: Execute shell commands directly with `!command` or open interactive shell with `!`
64
- - **🔧 Enhanced Bash Tool**: Advanced bash execution with timeouts, working directory, and environment variables
65
- - **🔧 JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
66
- - **⚡ Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
67
- - **🔄 Configuration Management**: New `/refresh` command to reload config without restart
68
- - **🧠 Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
69
- - **🛠️ New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
70
- - **📊 Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
61
+ ### Recent Updates (v0.0.18)
62
+
63
+ - **Advanced Agent Orchestration**: New orchestrator system for complex multi-step tasks with planning visibility
64
+ - **Background Task Manager**: Asynchronous background processing for long-running operations
65
+ - **Read-Only Agent**: Specialized agent for safe codebase exploration without modification risks
66
+ - **Planning Transparency**: See the AI's planning process before execution with detailed task breakdowns
67
+ - **Shell Command Support**: Execute shell commands directly with `!command` or open interactive shell with `!`
68
+ - **Enhanced Bash Tool**: Advanced bash execution with timeouts, working directory, and environment variables
69
+ - **JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
70
+ - **Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
71
+ - **Configuration Management**: New `/refresh` command to reload config without restart
72
+ - **Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
73
+ - **New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
74
+ - **Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
71
75
 
72
76
  ### Core Features
73
77
 
@@ -401,7 +405,14 @@ src/tunacode/
401
405
 
402
406
  ├── core/ # Core Application Logic
403
407
  │ ├── agents/ # AI Agent System
404
- │ │ └── main.py # Primary agent implementation (pydantic-ai)
408
+ │ │ ├── main.py # Primary agent implementation (pydantic-ai)
409
+ │ │ ├── orchestrator.py # Complex task orchestration and planning
410
+ │ │ ├── planner_schema.py # Planning data models
411
+ │ │ └── readonly.py # Read-only agent for safe exploration
412
+ │ ├── background/ # Background Task Management
413
+ │ │ └── manager.py # Async background task execution
414
+ │ ├── llm/ # LLM Integration
415
+ │ │ └── planner.py # LLM-based task planning
405
416
  │ ├── setup/ # Application Setup & Initialization
406
417
  │ │ ├── agent_setup.py # Agent configuration
407
418
  │ │ ├── base.py # Setup step base class
@@ -450,7 +461,7 @@ src/tunacode/
450
461
  ├── exceptions.py # Custom exceptions
451
462
  ├── types.py # Type definitions
452
463
  └── prompts/
453
- └── system.txt # System prompts for AI agent
464
+ └── system.md # System prompts for AI agent
454
465
  ```
455
466
 
456
467
  ### Key Components
@@ -459,6 +470,8 @@ src/tunacode/
459
470
  | -------------------- | ------------------------ | ------------------------------- |
460
471
  | **CLI Layer** | Command parsing and REPL | `cli/main.py`, `cli/repl.py` |
461
472
  | **Agent System** | AI-powered assistance | `core/agents/main.py` |
473
+ | **Orchestrator** | Complex task planning | `core/agents/orchestrator.py` |
474
+ | **Background Tasks** | Async task execution | `core/background/manager.py` |
462
475
  | **Tool System** | File/command operations | `tools/*.py` |
463
476
  | **State Management** | Session state tracking | `core/state.py` |
464
477
  | **UI Framework** | Rich terminal interface | `ui/output.py`, `ui/console.py` |
@@ -523,6 +536,9 @@ While TunaCode builds on the foundation of sidekick-cli, we've made several arch
523
536
 
524
537
  - **JSON Tool Parsing Fallback**: Added fallback parsing for when API providers fail with structured tool calling
525
538
  - **Parallel Search Tools**: New `bash` and `grep` tools with parallel execution for codebase navigation
539
+ - **Agent Orchestration**: Advanced orchestrator for complex multi-step tasks with planning transparency
540
+ - **Background Processing**: Asynchronous task manager for long-running operations
541
+ - **Read-Only Agent**: Safe exploration mode that prevents accidental modifications
526
542
  - **ReAct Reasoning**: Implemented ReAct (Reasoning + Acting) patterns with configurable iteration limits
527
543
  - **Dynamic Configuration**: Added `/refresh` command and modified configuration management
528
544
  - **Safety Changes**: Removed automatic git commits and `/undo` command - requires explicit git usage
@@ -22,16 +22,20 @@
22
22
 
23
23
  ---
24
24
 
25
- ### Recent Updates (v0.0.15)
26
-
27
- - **🐚 Shell Command Support**: Execute shell commands directly with `!command` or open interactive shell with `!`
28
- - **🔧 Enhanced Bash Tool**: Advanced bash execution with timeouts, working directory, and environment variables
29
- - **🔧 JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
30
- - **⚡ Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
31
- - **🔄 Configuration Management**: New `/refresh` command to reload config without restart
32
- - **🧠 Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
33
- - **🛠️ New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
34
- - **📊 Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
25
+ ### Recent Updates (v0.0.18)
26
+
27
+ - **Advanced Agent Orchestration**: New orchestrator system for complex multi-step tasks with planning visibility
28
+ - **Background Task Manager**: Asynchronous background processing for long-running operations
29
+ - **Read-Only Agent**: Specialized agent for safe codebase exploration without modification risks
30
+ - **Planning Transparency**: See the AI's planning process before execution with detailed task breakdowns
31
+ - **Shell Command Support**: Execute shell commands directly with `!command` or open interactive shell with `!`
32
+ - **Enhanced Bash Tool**: Advanced bash execution with timeouts, working directory, and environment variables
33
+ - **JSON Tool Parsing Fallback**: Automatic recovery when API providers fail with structured tool calling
34
+ - **Enhanced Reliability**: Fixed parameter naming issues that caused tool schema errors
35
+ - **Configuration Management**: New `/refresh` command to reload config without restart
36
+ - **Improved ReAct Reasoning**: Enhanced iteration limits (now defaults to 20) and better thought processing
37
+ - **New Debug Commands**: `/parsetools` for manual JSON parsing, `/iterations` for controlling reasoning depth
38
+ - **Better Error Recovery**: Multiple fallback mechanisms for various failure scenarios
35
39
 
36
40
  ### Core Features
37
41
 
@@ -365,7 +369,14 @@ src/tunacode/
365
369
 
366
370
  ├── core/ # Core Application Logic
367
371
  │ ├── agents/ # AI Agent System
368
- │ │ └── main.py # Primary agent implementation (pydantic-ai)
372
+ │ │ ├── main.py # Primary agent implementation (pydantic-ai)
373
+ │ │ ├── orchestrator.py # Complex task orchestration and planning
374
+ │ │ ├── planner_schema.py # Planning data models
375
+ │ │ └── readonly.py # Read-only agent for safe exploration
376
+ │ ├── background/ # Background Task Management
377
+ │ │ └── manager.py # Async background task execution
378
+ │ ├── llm/ # LLM Integration
379
+ │ │ └── planner.py # LLM-based task planning
369
380
  │ ├── setup/ # Application Setup & Initialization
370
381
  │ │ ├── agent_setup.py # Agent configuration
371
382
  │ │ ├── base.py # Setup step base class
@@ -414,7 +425,7 @@ src/tunacode/
414
425
  ├── exceptions.py # Custom exceptions
415
426
  ├── types.py # Type definitions
416
427
  └── prompts/
417
- └── system.txt # System prompts for AI agent
428
+ └── system.md # System prompts for AI agent
418
429
  ```
419
430
 
420
431
  ### Key Components
@@ -423,6 +434,8 @@ src/tunacode/
423
434
  | -------------------- | ------------------------ | ------------------------------- |
424
435
  | **CLI Layer** | Command parsing and REPL | `cli/main.py`, `cli/repl.py` |
425
436
  | **Agent System** | AI-powered assistance | `core/agents/main.py` |
437
+ | **Orchestrator** | Complex task planning | `core/agents/orchestrator.py` |
438
+ | **Background Tasks** | Async task execution | `core/background/manager.py` |
426
439
  | **Tool System** | File/command operations | `tools/*.py` |
427
440
  | **State Management** | Session state tracking | `core/state.py` |
428
441
  | **UI Framework** | Rich terminal interface | `ui/output.py`, `ui/console.py` |
@@ -487,6 +500,9 @@ While TunaCode builds on the foundation of sidekick-cli, we've made several arch
487
500
 
488
501
  - **JSON Tool Parsing Fallback**: Added fallback parsing for when API providers fail with structured tool calling
489
502
  - **Parallel Search Tools**: New `bash` and `grep` tools with parallel execution for codebase navigation
503
+ - **Agent Orchestration**: Advanced orchestrator for complex multi-step tasks with planning transparency
504
+ - **Background Processing**: Asynchronous task manager for long-running operations
505
+ - **Read-Only Agent**: Safe exploration mode that prevents accidental modifications
490
506
  - **ReAct Reasoning**: Implemented ReAct (Reasoning + Acting) patterns with configurable iteration limits
491
507
  - **Dynamic Configuration**: Added `/refresh` command and modified configuration management
492
508
  - **Safety Changes**: Removed automatic git commits and `/undo` command - requires explicit git usage
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tunacode-cli"
7
- version = "0.0.18"
7
+ version = "0.0.21"
8
8
  description = "Your agentic CLI developer."
9
9
  keywords = ["cli", "agent", "development", "automation"]
10
10
  readme = "README.md"
@@ -167,6 +167,39 @@ class ThoughtsCommand(SimpleCommand):
167
167
  await ui.success(f"Thought display {status}")
168
168
 
169
169
 
170
+ class ArchitectCommand(SimpleCommand):
171
+ """Toggle architect mode for task planning and orchestration."""
172
+
173
+ def __init__(self):
174
+ super().__init__(
175
+ CommandSpec(
176
+ name="architect",
177
+ aliases=["/architect"],
178
+ description="Toggle architect mode (task planning & orchestration)",
179
+ category=CommandCategory.DEBUG,
180
+ )
181
+ )
182
+
183
+ async def execute(self, args: List[str], context: CommandContext) -> None:
184
+ state = context.state_manager.session
185
+ if args:
186
+ arg = args[0].lower()
187
+ if arg in {"on", "1", "true"}:
188
+ state.architect_mode = True
189
+ elif arg in {"off", "0", "false"}:
190
+ state.architect_mode = False
191
+ else:
192
+ await ui.error("Usage: /architect [on|off]")
193
+ return
194
+ else:
195
+ state.architect_mode = not getattr(state, 'architect_mode', False)
196
+ status = "ON" if state.architect_mode else "OFF"
197
+ if state.architect_mode:
198
+ await ui.success(f"Architect mode {status} - Requests will be planned before execution")
199
+ else:
200
+ await ui.success(f"Architect mode {status} - Using direct execution")
201
+
202
+
170
203
  class IterationsCommand(SimpleCommand):
171
204
  """Configure maximum agent iterations for ReAct reasoning."""
172
205
 
@@ -720,6 +753,7 @@ class CommandRegistry:
720
753
  YoloCommand,
721
754
  DumpCommand,
722
755
  ThoughtsCommand,
756
+ ArchitectCommand,
723
757
  IterationsCommand,
724
758
  ClearCommand,
725
759
  FixCommand,
@@ -50,7 +50,9 @@ def main(
50
50
  await setup(run_setup, state_manager, cli_config)
51
51
  await repl(state_manager)
52
52
  except Exception as e:
53
- await ui.error(str(e))
53
+ import traceback
54
+
55
+ await ui.error(f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}")
54
56
 
55
57
  has_update, latest_version = await update_task
56
58
  if has_update:
@@ -17,6 +17,7 @@ from pydantic_ai.exceptions import UnexpectedModelBehavior
17
17
  from tunacode.configuration.settings import ApplicationSettings
18
18
  from tunacode.core.agents import main as agent
19
19
  from tunacode.core.agents.main import patch_tool_messages
20
+ from tunacode.core.agents.orchestrator import OrchestratorAgent
20
21
  from tunacode.core.tool_handler import ToolHandler
21
22
  from tunacode.exceptions import AgentError, UserAbortError, ValidationError
22
23
  from tunacode.ui import console as ui
@@ -160,41 +161,69 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
160
161
  True, state_manager.session.spinner, state_manager
161
162
  )
162
163
  try:
163
- # Expand @file references before sending to the agent
164
- try:
165
- from tunacode.utils.text_utils import expand_file_refs
166
-
167
- text = expand_file_refs(text)
168
- except ValueError as e:
169
- await ui.error(str(e))
170
- return
171
-
172
164
  # Patch any orphaned tool calls from previous requests before proceeding
173
165
  patch_tool_messages("Tool execution was interrupted", state_manager)
174
166
 
167
+ # Track message start for thoughts display
168
+ start_idx = len(state_manager.session.messages)
169
+
175
170
  # Create a partial function that includes state_manager
176
171
  def tool_callback_with_state(part, node):
177
172
  return _tool_handler(part, node, state_manager)
178
173
 
179
- start_idx = len(state_manager.session.messages)
180
- res = await agent.process_request(
181
- state_manager.session.current_model,
182
- text,
183
- state_manager,
184
- tool_callback=tool_callback_with_state,
185
- )
186
- if output:
187
- if state_manager.session.show_thoughts:
188
- new_msgs = state_manager.session.messages[start_idx:]
189
- for msg in new_msgs:
190
- if isinstance(msg, dict) and "thought" in msg:
191
- await ui.muted(f"THOUGHT: {msg['thought']}")
192
- # Check if result exists and has output
193
- if hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
194
- await ui.agent(res.result.output)
195
- else:
196
- # Fallback: show that the request was processed
197
- await ui.muted("Request completed")
174
+ # Check if architect mode is enabled
175
+ if getattr(state_manager.session, 'architect_mode', False):
176
+ # Expand @file references before sending to the orchestrator
177
+ try:
178
+ from tunacode.utils.text_utils import expand_file_refs
179
+
180
+ text = expand_file_refs(text)
181
+ except ValueError as e:
182
+ await ui.error(str(e))
183
+ return
184
+ # Use orchestrator for planning and execution
185
+ orchestrator = OrchestratorAgent(state_manager)
186
+ results = await orchestrator.run(text, state_manager.session.current_model)
187
+
188
+ if output:
189
+ # Process results from all sub-agents
190
+ for res in results:
191
+ # Check if result exists and has output
192
+ if hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
193
+ await ui.agent(res.result.output)
194
+
195
+ if not results:
196
+ # Fallback: show that the request was processed
197
+ await ui.muted("Request completed")
198
+ else:
199
+ # Expand @file references before sending to the agent
200
+ try:
201
+ from tunacode.utils.text_utils import expand_file_refs
202
+
203
+ text = expand_file_refs(text)
204
+ except ValueError as e:
205
+ await ui.error(str(e))
206
+ return
207
+
208
+ # Use normal agent processing
209
+ res = await agent.process_request(
210
+ state_manager.session.current_model,
211
+ text,
212
+ state_manager,
213
+ tool_callback=tool_callback_with_state,
214
+ )
215
+ if output:
216
+ if state_manager.session.show_thoughts:
217
+ new_msgs = state_manager.session.messages[start_idx:]
218
+ for msg in new_msgs:
219
+ if isinstance(msg, dict) and "thought" in msg:
220
+ await ui.muted(f"THOUGHT: {msg['thought']}")
221
+ # Check if result exists and has output
222
+ if hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
223
+ await ui.agent(res.result.output)
224
+ else:
225
+ # Fallback: show that the request was processed
226
+ await ui.muted("Request completed")
198
227
  except CancelledError:
199
228
  await ui.muted("Request cancelled")
200
229
  except UserAbortError:
@@ -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.18"
10
+ APP_VERSION = "0.0.21"
11
11
 
12
12
  # File patterns
13
13
  GUIDE_FILE_PATTERN = "{name}.md"
@@ -0,0 +1,12 @@
1
+ """Agent helper modules."""
2
+
3
+ from .main import get_or_create_agent, process_request
4
+ from .orchestrator import OrchestratorAgent
5
+ from .readonly import ReadOnlyAgent
6
+
7
+ __all__ = [
8
+ "process_request",
9
+ "get_or_create_agent",
10
+ "OrchestratorAgent",
11
+ "ReadOnlyAgent",
12
+ ]
@@ -134,7 +134,7 @@ def get_or_create_agent(model: ModelName, state_manager: StateManager) -> Pydant
134
134
  import os
135
135
  from pathlib import Path
136
136
 
137
- prompt_path = Path(__file__).parent.parent.parent / "prompts" / "system.txt"
137
+ prompt_path = Path(__file__).parent.parent.parent / "prompts" / "system.md"
138
138
  try:
139
139
  with open(prompt_path, "r", encoding="utf-8") as f:
140
140
  system_prompt = f.read().strip()
@@ -0,0 +1,99 @@
1
+ """Agent orchestration scaffolding.
2
+
3
+ This module defines an ``OrchestratorAgent`` class that demonstrates how
4
+ higher level planning and delegation could be layered on top of the existing
5
+ ``process_request`` workflow. The goal is to keep orchestration logic isolated
6
+ from the core agent implementation while reusing all current tooling and state
7
+ handling provided by ``main.process_request``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import itertools
14
+ from typing import List
15
+
16
+ from ...types import AgentRun, ModelName
17
+ from ..llm.planner import make_plan
18
+ from ..state import StateManager
19
+ from . import main as agent_main
20
+ from .planner_schema import Task
21
+ from .readonly import ReadOnlyAgent
22
+
23
+
24
+ class OrchestratorAgent:
25
+ """Plan and run a sequence of sub-agent tasks."""
26
+
27
+ def __init__(self, state_manager: StateManager):
28
+ self.state = state_manager
29
+
30
+ async def plan(self, request: str, model: ModelName) -> List[Task]:
31
+ """Plan tasks for a user request using the planner LLM."""
32
+
33
+ return await make_plan(request, model, self.state)
34
+
35
+ async def _run_sub_task(self, task: Task, model: ModelName) -> AgentRun:
36
+ """Execute a single task using an appropriate sub-agent."""
37
+ from rich.console import Console
38
+
39
+ console = Console()
40
+
41
+ # Show which task is being executed
42
+ task_type = "WRITE" if task.mutate else "READ"
43
+ console.print(f"\n[dim][Task {task.id}] {task_type}[/dim]")
44
+ console.print(f"[dim] → {task.description}[/dim]")
45
+
46
+ if task.mutate:
47
+ agent_main.get_or_create_agent(model, self.state)
48
+ result = await agent_main.process_request(model, task.description, self.state)
49
+ else:
50
+ agent = ReadOnlyAgent(model, self.state)
51
+ result = await agent.process_request(task.description)
52
+
53
+ console.print(f"[dim][Task {task.id}] Complete[/dim]")
54
+ return result
55
+
56
+ async def run(self, request: str, model: ModelName | None = None) -> List[AgentRun]:
57
+ """Plan and execute a user request.
58
+
59
+ Parameters
60
+ ----------
61
+ request:
62
+ The high level user request to process.
63
+ model:
64
+ Optional model name to use for sub agents. Defaults to the current
65
+ session model.
66
+ """
67
+ from rich.console import Console
68
+
69
+ console = Console()
70
+ model = model or self.state.session.current_model
71
+
72
+ # Show orchestrator is starting
73
+ console.print(
74
+ "\n[cyan]Orchestrator Mode: Analyzing request and creating execution plan...[/cyan]"
75
+ )
76
+
77
+ tasks = await self.plan(request, model)
78
+
79
+ # Show execution is starting
80
+ console.print(f"\n[cyan]Executing plan with {len(tasks)} tasks...[/cyan]")
81
+
82
+ results: List[AgentRun] = []
83
+ for mutate_flag, group in itertools.groupby(tasks, key=lambda t: t.mutate):
84
+ if mutate_flag:
85
+ for t in group:
86
+ results.append(await self._run_sub_task(t, model))
87
+ else:
88
+ # Show parallel execution
89
+ task_list = list(group)
90
+ if len(task_list) > 1:
91
+ console.print(
92
+ f"\n[dim][Parallel Execution] Running {len(task_list)} read-only tasks concurrently...[/dim]"
93
+ )
94
+ coros = [self._run_sub_task(t, model) for t in task_list]
95
+ results.extend(await asyncio.gather(*coros))
96
+
97
+ console.print("\n[green]Orchestrator completed all tasks successfully![/green]")
98
+
99
+ return results
@@ -0,0 +1,9 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class Task(BaseModel):
5
+ """Single sub-task generated by the planner."""
6
+
7
+ id: int = Field(..., description="1-based task index in execution order")
8
+ description: str = Field(..., description="What the sub-agent must do")
9
+ mutate: bool = Field(..., description="True if the task changes code")
@@ -0,0 +1,51 @@
1
+ """Read-only agent implementation for non-mutating operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from ...tools.grep import grep
8
+ from ...tools.read_file import read_file
9
+ from ...types import AgentRun, ModelName
10
+ from ..state import StateManager
11
+
12
+ if TYPE_CHECKING:
13
+ from ...types import PydanticAgent
14
+
15
+
16
+ class ReadOnlyAgent:
17
+ """Agent configured with read-only tools for analysis tasks."""
18
+
19
+ def __init__(self, model: ModelName, state_manager: StateManager):
20
+ self.model = model
21
+ self.state_manager = state_manager
22
+ self._agent: PydanticAgent | None = None
23
+
24
+ def _get_agent(self) -> PydanticAgent:
25
+ """Lazily create the agent with read-only tools."""
26
+ if self._agent is None:
27
+ from .main import get_agent_tool
28
+
29
+ Agent, Tool = get_agent_tool()
30
+
31
+ # Create agent with only read-only tools
32
+ self._agent = Agent(
33
+ model=self.model,
34
+ system_prompt="You are a read-only assistant. You can analyze and read files but cannot modify them.",
35
+ tools=[
36
+ Tool(read_file),
37
+ Tool(grep),
38
+ ],
39
+ )
40
+ return self._agent
41
+
42
+ async def process_request(self, request: str) -> AgentRun:
43
+ """Process a request using only read-only tools."""
44
+ agent = self._get_agent()
45
+
46
+ # Use iter() like main.py does to get the full run context
47
+ async with agent.iter(request) as agent_run:
48
+ async for _ in agent_run:
49
+ pass # Let it complete
50
+
51
+ return agent_run
@@ -0,0 +1,36 @@
1
+ """Asynchronous background task management utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import uuid
7
+ from collections import defaultdict
8
+ from typing import Awaitable, Callable, Dict, List
9
+
10
+
11
+ class BackgroundTaskManager:
12
+ """Simple manager for background asyncio tasks."""
13
+
14
+ def __init__(self) -> None:
15
+ self.tasks: Dict[str, asyncio.Task] = {}
16
+ self.listeners: Dict[str, List[Callable[[asyncio.Task], None]]] = defaultdict(list)
17
+
18
+ def spawn(self, coro: Awaitable, *, name: str | None = None) -> str:
19
+ task_id = name or uuid.uuid4().hex[:8]
20
+ task = asyncio.create_task(coro, name=task_id)
21
+ self.tasks[task_id] = task
22
+ task.add_done_callback(self._notify)
23
+ return task_id
24
+
25
+ def _notify(self, task: asyncio.Task) -> None:
26
+ for cb in self.listeners.get(task.get_name(), []):
27
+ cb(task)
28
+
29
+ async def shutdown(self) -> None:
30
+ for task in self.tasks.values():
31
+ task.cancel()
32
+ if self.tasks:
33
+ await asyncio.gather(*self.tasks.values(), return_exceptions=True)
34
+
35
+
36
+ BG_MANAGER = BackgroundTaskManager()
@@ -0,0 +1,63 @@
1
+ import json
2
+ from typing import List
3
+
4
+ from ...types import ModelName
5
+ from ..agents.planner_schema import Task
6
+ from ..state import StateManager
7
+
8
+ _SYSTEM = """You are a senior software project planner.
9
+
10
+ Your job is to break down a USER_REQUEST into a logical sequence of tasks.
11
+
12
+ Guidelines:
13
+ 1. Use the FEWEST tasks possible - combine related operations
14
+ 2. Order tasks logically - reads before writes, understand before modify
15
+ 3. Mark read-only tasks (reading files, searching, analyzing) as mutate: false
16
+ 4. Mark modifying tasks (writing, updating, creating files) as mutate: true
17
+ 5. Write clear, actionable descriptions
18
+
19
+ Each task MUST have:
20
+ - id: Sequential number starting from 1
21
+ - description: Clear description of what needs to be done
22
+ - mutate: true if the task modifies files/code, false if it only reads/analyzes
23
+
24
+ Return ONLY a valid JSON array of tasks, no other text.
25
+ Example:
26
+ [
27
+ {"id": 1, "description": "Read the main.py file to understand the structure", "mutate": false},
28
+ {"id": 2, "description": "Update the function to handle edge cases", "mutate": true}
29
+ ]"""
30
+
31
+
32
+ async def make_plan(request: str, model: ModelName, state_manager: StateManager) -> List[Task]:
33
+ """Generate an execution plan from a user request using TunaCode's LLM infrastructure."""
34
+ # Lazy import to avoid circular dependencies
35
+ from rich.console import Console
36
+
37
+ from ..agents.main import get_agent_tool
38
+
39
+ console = Console()
40
+ Agent, _ = get_agent_tool()
41
+
42
+ # Show planning is starting
43
+ console.print("\n[dim][Planning] Breaking down request into tasks...[/dim]")
44
+
45
+ # Create a simple planning agent
46
+ planner = Agent(
47
+ model=model,
48
+ system_prompt=_SYSTEM,
49
+ result_type=List[Task],
50
+ )
51
+
52
+ # Get the plan from the agent
53
+ result = await planner.run(request)
54
+ tasks = result.data
55
+
56
+ # Display the plan
57
+ console.print(f"[dim][Planning] Generated {len(tasks)} tasks:[/dim]")
58
+ for task in tasks:
59
+ task_type = "WRITE" if task.mutate else "READ"
60
+ console.print(f"[dim] Task {task.id}: {task_type} - {task.description}[/dim]")
61
+ console.print("")
62
+
63
+ return tasks