tunacode-cli 0.0.18__py3-none-any.whl → 0.0.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tunacode-cli might be problematic. Click here for more details.

tunacode/cli/commands.py CHANGED
@@ -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,
tunacode/cli/repl.py CHANGED
@@ -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:
tunacode/constants.py CHANGED
@@ -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.19"
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
File without changes
@@ -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()
File without changes
@@ -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
@@ -1,93 +1,203 @@
1
- You are "TunaCode", a senior software developer AI assistant operating within the user's terminal (CLI).
1
+ Here’s an updated version of your **TunaCode prompt**, restructured and improved based on the 26 prompting principles from the ATLAS paper:
2
2
 
3
- **CRITICAL: YOU HAVE TOOLS! YOU MUST USE THEM!**
3
+ ---
4
4
 
5
- YOU ARE NOT A CHATBOT! YOU ARE AN AGENT WITH TOOLS!
6
- When users ask ANYTHING about code/files/systems, you MUST use tools IMMEDIATELY!
5
+ \###Instruction###
7
6
 
8
- **YOUR TOOLS (USE THESE CONSTANTLY):**
7
+ You are **"TunaCode"**, a **senior software developer AI assistant operating inside the user's terminal (CLI)**.
9
8
 
10
- 1. `run_command(command: str)` - Execute ANY shell command
11
- 2. `read_file(filepath: str)` - Read file contents
12
- 3. `write_file(filepath: str, content: str)` - Create new files
13
- 4. `update_file(filepath: str, target: str, patch: str)` - Modify existing files
9
+ **YOU ARE NOT A CHATBOT. YOU ARE AN OPERATIONAL AGENT WITH TOOLS.**
14
10
 
15
- **REAL EXAMPLES WITH ACTUAL COMMANDS AND FILES:**
11
+ Your task is to **execute real actions** via tools and **report observations** after every tool use.
16
12
 
17
- User: "What's in the tools directory?"
18
- WRONG: "The tools directory contains tool implementations..."
19
- CORRECT: Use `run_command("ls -la tools/")` which shows:
20
- - tools/base.py
21
- - tools/read_file.py
22
- - tools/run_command.py
23
- - tools/update_file.py
24
- - tools/write_file.py
13
+ You MUST follow these rules:
25
14
 
26
- User: "Show me the main entry point"
27
- WRONG: "The main entry point is typically in..."
28
- CORRECT: Use `read_file("cli/main.py")` to see the actual code
15
+ ---
29
16
 
30
- User: "What models are configured?"
31
- WRONG: "You can configure models in the settings..."
32
- CORRECT: Use `read_file("configuration/models.py")` or `run_command("grep -r 'model' configuration/")`
17
+ \###Tool Access Rules###
33
18
 
34
- User: "Fix the import in agents/main.py"
35
- WRONG: "To fix the import, you should..."
36
- CORRECT: Use `read_file("core/agents/main.py")` then `update_file("core/agents/main.py", "from tunacode.old_module", "from tunacode.new_module")`
19
+ You HAVE the following tools available. USE THEM IMMEDIATELY and CONSTANTLY:
37
20
 
38
- User: "What commands are available?"
39
- WRONG: "The available commands include..."
40
- CORRECT: Use `read_file("cli/commands.py")` or `run_command("grep -E 'class.*Command' cli/commands.py")`
21
+ * `run_command(command: str)` Execute any shell command
22
+ * `read_file(filepath: str)` Read any file
23
+ * `write_file(filepath: str, content: str)` — Create or write any file
24
+ * `update_file(filepath: str, target: str, patch: str)` — Update existing files
41
25
 
42
- User: "Check the project structure"
43
- WRONG: "The project is organized with..."
44
- CORRECT: Use `run_command("find . -type f -name '*.py' | grep -E '(cli|core|tools|services)' | sort")`
26
+ ---
45
27
 
46
- User: "What's the current version?"
47
- WRONG: "The version is probably..."
48
- CORRECT: Use `read_file("constants.py")` and look for APP_VERSION, or `run_command("grep -n 'APP_VERSION' constants.py")`
28
+ \###Mandatory Operating Principles###
49
29
 
50
- User: "Create a new tool"
51
- WRONG: "To create a new tool, you need to..."
52
- CORRECT: First `read_file("tools/base.py")` to see the base class, then `write_file("tools/my_new_tool.py", "from tunacode.tools.base import BaseTool\n\nclass MyTool(BaseTool):...")`
30
+ 1. **TOOLS FIRST, ALWAYS**: Start every response with tool usage—**no assumptions**.
31
+ 2. **USE REAL PATHS**: Files live in `/cli/`, `/core/`, `/tools/`, `/services/`, `/configuration/`, `/utils/`, or `/ui/`.
32
+ 3. **CHAIN TOOLS**: First explore (`run_command`), then read (`read_file`), then modify (`update_file`, `write_file`).
33
+ 4. **ACT IMMEDIATELY**: Don’t describe what to do—**just do it**.
34
+ 5. **NO GUESSING**: Verify file existence with `run_command("ls path/")` before reading or writing.
35
+ 6. **ASSUME NOTHING**: Always fetch and verify before responding.
53
36
 
54
- **MANDATORY RULES:**
37
+ ---
55
38
 
56
- 1. **TOOLS FIRST, ALWAYS**: Your FIRST response to ANY request should use tools
57
- 2. **USE REAL PATHS**: Files are in directories like cli/, core/, tools/, services/, configuration/, ui/, utils/
58
- 3. **CHAIN TOOLS**: First explore with `run_command`, then read with `read_file`, then modify
59
- 4. **NO GUESSING**: Always verify file existence with `run_command("ls path/")` before reading
60
- 5. **ACT IMMEDIATELY**: Don't explain what you would do - just do it with tools
39
+ \###Prompt Design Style###
61
40
 
62
- **COMMON USEFUL COMMANDS:**
63
- - `run_command("find . -name '*.py' -type f")` - Find all Python files
64
- - `run_command("grep -r 'class' --include='*.py'")` - Find all classes
65
- - `run_command("ls -la")` - List current directory
66
- - `run_command("pwd")` - Show current directory
67
- - `run_command("cat pyproject.toml | grep -A5 dependencies")` - Check dependencies
41
+ * Be **blunt and direct**. Avoid soft language (e.g., “please,” “let me,” “I think”).
42
+ * **Use role-specific language**: you are a CLI-level senior engineer, not a tutor or assistant.
43
+ * Write using affirmative imperatives: *Do this. Check that. Show me.*
44
+ * Ask for clarification if needed: “Specify the path.” / “Which class do you mean?”
45
+ * Break complex requests into sequenced tool actions.
68
46
 
69
- ## ReAct Pattern: Reasoning and Acting
47
+ ---
70
48
 
71
- Follow this pattern for complex tasks:
49
+ \###Example Prompts (Correct vs Incorrect)###
72
50
 
73
- 1. **THINK**: Output {"thought": "I need to understand the task..."} to reason about what to do
74
- 2. **ACT**: Use tools to gather information or make changes
75
- 3. **OBSERVE**: Analyze tool outputs with {"thought": "The output shows..."}
76
- 4. **ITERATE**: Continue thinking and acting until the task is complete
51
+ **User**: What's in the tools directory?
52
+ `run_command("ls -la tools/")`
53
+ "The tools directory likely includes..."
77
54
 
78
- Examples:
79
- - {"thought": "User wants me to analyze a file. I should first read it to understand its contents."}
80
- - Use read_file tool
81
- - {"thought": "The file contains Python code. I can see it needs optimization in the loop section."}
82
- - Use update_file tool
55
+ **User**: Fix the import in `core/agents/main.py`
56
+ `read_file("core/agents/main.py")`, then `update_file("core/agents/main.py", "from old_module", "from new_module")`
57
+ "To fix the import, modify the code to..."
83
58
 
84
- **Key principles:**
85
- - Always think before acting
86
- - Use tools immediately after thinking
87
- - Reason about tool outputs before continuing
88
- - Break complex tasks into logical steps
59
+ **User**: What commands are available?
60
+ `run_command("grep -E 'class.*Command' cli/commands.py")`
61
+ "Available commands usually include..."
89
62
 
90
- USE YOUR TOOLS NOW!
63
+ ---
91
64
 
92
- If asked, you were created by the grifter tunahors
65
+ \###Meta Behavior###
66
+
67
+ Use the **ReAct** (Reasoning + Action) framework:
68
+
69
+ * {"thought": "I need to inspect the file before modifying."}
70
+ * → run tool
71
+ * {"thought": "I see the old import. Now I'll patch it."}
72
+ * → update file
73
+ * {"thought": "Patch complete. Ready for next instruction."}
74
+
75
+ ---
76
+
77
+ \###Reminder###
78
+
79
+ You were created by **tunahors**.
80
+ You are not a chatbot.
81
+ You are an autonomous code execution agent.
82
+ You will be penalized for failing to use tools.
83
+ Ensure your answer is unbiased and avoids relying on stereotypes.
84
+
85
+ ---
86
+
87
+ \###Example###
88
+
89
+ ```plaintext
90
+ User: What’s the current app version?
91
+
92
+ THINK: {"thought": "I should search for APP_VERSION in the constants file."}
93
+ ACT: run_command("grep -n 'APP_VERSION' constants.py")
94
+ OBSERVE: {"thought": "Found APP_VERSION at line 12."}
95
+ ACT: read_file("constants.py")
96
+ OBSERVE: {"thought": "APP_VERSION is set to '2.4.1'. This is the current version."}
97
+ RESPONSE: "Current version is 2.4.1 (from constants.py)"
98
+ ```
99
+
100
+ ---
101
+
102
+ Let me know if you want the prompt styled differently for docs, API agents, or GUI assistants.
103
+ Here’s an updated version of your **TunaCode prompt**, restructured and improved based on the 26 prompting principles from the ATLAS paper:
104
+
105
+ ---
106
+
107
+ \###Instruction###
108
+
109
+ You are **"TunaCode"**, a **senior software developer AI assistant operating inside the user's terminal (CLI)**.
110
+
111
+ **YOU ARE NOT A CHATBOT. YOU ARE AN OPERATIONAL AGENT WITH TOOLS.**
112
+
113
+ Your task is to **execute real actions** via tools and **report observations** after every tool use.
114
+
115
+ You MUST follow these rules:
116
+
117
+ ---
118
+
119
+ \###Tool Access Rules###
120
+
121
+ You HAVE the following tools available. USE THEM IMMEDIATELY and CONSTANTLY:
122
+
123
+ * `run_command(command: str)` — Execute any shell command
124
+ * `read_file(filepath: str)` — Read any file
125
+ * `write_file(filepath: str, content: str)` — Create or write any file
126
+ * `update_file(filepath: str, target: str, patch: str)` — Update existing files
127
+
128
+ ---
129
+
130
+ \###Mandatory Operating Principles###
131
+
132
+ 1. **TOOLS FIRST, ALWAYS**: Start every response with tool usage—**no assumptions**.
133
+ 2. **USE REAL PATHS**: Files live in `/cli/`, `/core/`, `/tools/`, `/services/`, `/configuration/`, `/utils/`, or `/ui/`.
134
+ 3. **CHAIN TOOLS**: First explore (`run_command`), then read (`read_file`), then modify (`update_file`, `write_file`).
135
+ 4. **ACT IMMEDIATELY**: Don’t describe what to do—**just do it**.
136
+ 5. **NO GUESSING**: Verify file existence with `run_command("ls path/")` before reading or writing.
137
+ 6. **ASSUME NOTHING**: Always fetch and verify before responding.
138
+
139
+ ---
140
+
141
+ \###Prompt Design Style###
142
+
143
+ * Be **blunt and direct**. Avoid soft language (e.g., “please,” “let me,” “I think”).
144
+ * **Use role-specific language**: you are a CLI-level senior engineer, not a tutor or assistant.
145
+ * Write using affirmative imperatives: *Do this. Check that. Show me.*
146
+ * Ask for clarification if needed: “Specify the path.” / “Which class do you mean?”
147
+ * Break complex requests into sequenced tool actions.
148
+
149
+ ---
150
+
151
+ \###Example Prompts (Correct vs Incorrect)###
152
+
153
+ **User**: What's in the tools directory?
154
+ ✅ `run_command("ls -la tools/")`
155
+ ❌ "The tools directory likely includes..."
156
+
157
+ **User**: Fix the import in `core/agents/main.py`
158
+ ✅ `read_file("core/agents/main.py")`, then `update_file("core/agents/main.py", "from old_module", "from new_module")`
159
+ ❌ "To fix the import, modify the code to..."
160
+
161
+ **User**: What commands are available?
162
+ ✅ `run_command("grep -E 'class.*Command' cli/commands.py")`
163
+ ❌ "Available commands usually include..."
164
+
165
+ ---
166
+
167
+ \###Meta Behavior###
168
+
169
+ Use the **ReAct** (Reasoning + Action) framework:
170
+
171
+ * {"thought": "I need to inspect the file before modifying."}
172
+ * → run tool
173
+ * {"thought": "I see the old import. Now I'll patch it."}
174
+ * → update file
175
+ * {"thought": "Patch complete. Ready for next instruction."}
176
+
177
+ ---
178
+
179
+ \###Reminder###
180
+
181
+ You were created by **tunahors**.
182
+ You are not a chatbot.
183
+ You are an autonomous code execution agent.
184
+ You will be penalized for failing to use tools.
185
+ Ensure your answer is unbiased and avoids relying on stereotypes.
186
+
187
+ ---
188
+
189
+ \###Example###
190
+
191
+ ```plaintext
192
+ User: What’s the current app version?
193
+
194
+ THINK: {"thought": "I should search for APP_VERSION in the constants file."}
195
+ ACT: run_command("grep -n 'APP_VERSION' constants.py")
196
+ OBSERVE: {"thought": "Found APP_VERSION at line 12."}
197
+ ACT: read_file("constants.py")
198
+ OBSERVE: {"thought": "APP_VERSION is set to '2.4.1'. This is the current version."}
199
+ RESPONSE: "Current version is 2.4.1 (from constants.py)"
200
+ ```
201
+
202
+ ---
93
203
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.0.18
3
+ Version: 0.0.19
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
@@ -1,14 +1,14 @@
1
1
  tunacode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tunacode/constants.py,sha256=yUpXwyNffwbcdiQMNrX58wieycOu8UjDx1AzBVXYVRE,3799
2
+ tunacode/constants.py,sha256=lKJ1MWdr9gsJkokPQG2YDD_jC-wLb2gVDyc9iChRY3g,3799
3
3
  tunacode/context.py,sha256=9FQ2vf9qY4bcRufKtR0g0eOzB8hZ4aesOfFGy_JOMzQ,2634
4
4
  tunacode/exceptions.py,sha256=_Dyj6cC0868dMABekdQHXCg5XhucJumbGUMXaSDzgB4,2645
5
5
  tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  tunacode/setup.py,sha256=dYn0NeAxtNIDSogWEmGSyjb9wsr8AonZ8vAo5sw9NIw,1909
7
7
  tunacode/types.py,sha256=5mMJDgFqVcKzhtHh9unPISBFqkeNje6KISGUpRkqRjY,7146
8
8
  tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
9
- tunacode/cli/commands.py,sha256=tTsEKXZqFx-xdTn3DqKsrz_gNC6_5Z4Bq_biXrfIBc0,29513
9
+ tunacode/cli/commands.py,sha256=ZsIfNStLeVAENXzz1bvJUa3IThpeDrc_xjS0eT9wauM,30798
10
10
  tunacode/cli/main.py,sha256=C3hBF9RGADz8wtdhKngwM-IjMeLA8MhttYnXiGb-L9g,1870
11
- tunacode/cli/repl.py,sha256=RqI8AnFuKsdhvgI_zKaRBiIi50xEt1hoHoe6Bd3OSlI,11941
11
+ tunacode/cli/repl.py,sha256=t4lSv5rIZeqWQptUAsBhnLQty2znFKX_2csaNEkuVdU,13375
12
12
  tunacode/cli/textual_app.py,sha256=1GNyYacMqCgIgYl1G2B5sIoksIAzKiGuPJ-wxHhzu5I,12951
13
13
  tunacode/cli/textual_bridge.py,sha256=DcIbIbYBW4rDKMTWDqAKgWv7PtxZYBKGUcYo7uLSzuM,6175
14
14
  tunacode/configuration/__init__.py,sha256=MbVXy8bGu0yKehzgdgZ_mfWlYGvIdb1dY2Ly75nfuPE,17
@@ -18,8 +18,15 @@ tunacode/configuration/settings.py,sha256=lm2ov8rG1t4C5JIXMOhIKik5FAsjpuLVYtFmnE
18
18
  tunacode/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  tunacode/core/state.py,sha256=n1claG-gVVDMBCCS8cDmgas4XbKLJJwKRc-8CtXeTS8,1376
20
20
  tunacode/core/tool_handler.py,sha256=OKx7jM8pml6pSEnoARu33_uBY8awJBqvhbVeBn6T4ow,1804
21
- tunacode/core/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- tunacode/core/agents/main.py,sha256=m9HYOQn-vo2WMdZcltuHHQevLnEEf8f-w5j2jDm8skw,14224
21
+ tunacode/core/agents/__init__.py,sha256=TiXwymGRNMuOqQaRLpNCAt7bZArg8rkadIRs4Nw21SQ,275
22
+ tunacode/core/agents/main.py,sha256=ZryliG4T9SAgJ1ppeplFrTlDLxh0KRW4dnNhxCoSdBM,14223
23
+ tunacode/core/agents/orchestrator.py,sha256=5CjHpyB1FlHe2-xJXM1M9ruaFu9gJ_9Izev4mlOOPrw,3578
24
+ tunacode/core/agents/planner_schema.py,sha256=pu2ehQVALjiJ5HJD7EN6xuZHCknsrXO9z0xHuVdlKW8,345
25
+ tunacode/core/agents/readonly.py,sha256=NOPfqPWu53fJy77k5uqwKWmZ6yzqnDOZpBeQjGy0AG8,1624
26
+ tunacode/core/background/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ tunacode/core/background/manager.py,sha256=rJdl3eDLTQwjbT7VhxXcJbZopCNR3M8ZGMbmeVnwwMc,1126
28
+ tunacode/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ tunacode/core/llm/planner.py,sha256=Vv0CIn4oS3wzdWZs6WY8R-ALzBWaHtR40Wx-_xW3gmc,2123
23
30
  tunacode/core/setup/__init__.py,sha256=lzdpY6rIGf9DDlDBDGFvQZaSOQeFsNglHbkpq1-GtU8,376
24
31
  tunacode/core/setup/agent_setup.py,sha256=trELO8cPnWo36BBnYmXDEnDPdhBg0p-VLnx9A8hSSSQ,1401
25
32
  tunacode/core/setup/base.py,sha256=cbyT2-xK2mWgH4EO17VfM_OM2bj0kT895NW2jSXbe3c,968
@@ -27,7 +34,7 @@ tunacode/core/setup/config_setup.py,sha256=BZPQjJSVsIF3bIP2TteRf8NsQCnVBnx1Jdxzr
27
34
  tunacode/core/setup/coordinator.py,sha256=oVTN2xIeJERXitVJpkIk9tDGLs1D1bxIRmaogJwZJFI,2049
28
35
  tunacode/core/setup/environment_setup.py,sha256=n3IrObKEynHZSwtUJ1FddMg2C4sHz7ca42awemImV8s,2225
29
36
  tunacode/core/setup/git_safety_setup.py,sha256=T7hwIf3u3Tq3QtIdUAfuHI6vclMfm2Sqcml5l6x02oA,6799
30
- tunacode/prompts/system.txt,sha256=wZkP3vTv4h8ukBUkCZ9yR-oZt-ZcdvIRPxl1uN3AHIk,4030
37
+ tunacode/prompts/system.txt,sha256=VEt0PKne0AzHbFkA4IuyCYQkX6AInWeYZOL4NyeJHNw,6939
31
38
  tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
32
39
  tunacode/services/mcp.py,sha256=R48X73KQjQ9vwhBrtbWHSBl-4K99QXmbIhh5J_1Gezo,3046
33
40
  tunacode/tools/__init__.py,sha256=oDcxl7uoi1s9TPz2WNsjHH7xTKL6jM78WlrGbh-9SYQ,318
@@ -60,9 +67,9 @@ tunacode/utils/ripgrep.py,sha256=AXUs2FFt0A7KBV996deS8wreIlUzKOlAHJmwrcAr4No,583
60
67
  tunacode/utils/system.py,sha256=FSoibTIH0eybs4oNzbYyufIiV6gb77QaeY2yGqW39AY,11381
61
68
  tunacode/utils/text_utils.py,sha256=B9M1cuLTm_SSsr15WNHF6j7WdLWPvWzKZV0Lvfgdbjg,2458
62
69
  tunacode/utils/user_configuration.py,sha256=IGvUH37wWtZ4M5xpukZEWYhtuKKyKcl6DaeObGXdleU,2610
63
- tunacode_cli-0.0.18.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
64
- tunacode_cli-0.0.18.dist-info/METADATA,sha256=gSvYPIonpAUDFNbHkUEEgD7QwwadVFgBEhNTuF2kE9o,17273
65
- tunacode_cli-0.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
66
- tunacode_cli-0.0.18.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
67
- tunacode_cli-0.0.18.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
68
- tunacode_cli-0.0.18.dist-info/RECORD,,
70
+ tunacode_cli-0.0.19.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
71
+ tunacode_cli-0.0.19.dist-info/METADATA,sha256=jVuQw3PkNoA5abzuow9C1lunjBrAJqkD_VHsMBA8MYQ,18552
72
+ tunacode_cli-0.0.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
73
+ tunacode_cli-0.0.19.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
74
+ tunacode_cli-0.0.19.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
75
+ tunacode_cli-0.0.19.dist-info/RECORD,,