tunacode-cli 0.0.23__tar.gz → 0.0.24__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.23/src/tunacode_cli.egg-info → tunacode_cli-0.0.24}/PKG-INFO +20 -1
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/README.md +18 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/pyproject.toml +2 -1
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/main.py +4 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/repl.py +9 -2
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/constants.py +1 -1
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24/src/tunacode_cli.egg-info}/PKG-INFO +20 -1
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/requires.txt +1 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_architect_integration.py +22 -46
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_architect_simple.py +5 -37
- tunacode_cli-0.0.24/tests/test_fast_glob_search.py +111 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_orchestrator_planning_visibility.py +23 -15
- tunacode_cli-0.0.24/tests/test_react_thoughts.py +92 -0
- tunacode_cli-0.0.23/tests/test_fast_glob_search.py +0 -191
- tunacode_cli-0.0.23/tests/test_react_thoughts.py +0 -149
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/CLAUDE.md +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/LICENSE +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/MANIFEST.in +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/TUNACODE.md +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/setup.cfg +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/commands.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/textual_app.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/cli/textual_bridge.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/configuration/defaults.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/configuration/models.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/context.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/agents/main.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/agents/orchestrator.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/agents/planner_schema.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/agents/readonly.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/background/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/background/manager.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/llm/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/llm/planner.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/agent_setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/base.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/config_setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/coordinator.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/environment_setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/setup/git_safety_setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/state.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/core/tool_handler.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/exceptions.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/prompts/system.md +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/prompts/system.txt +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/services/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/services/mcp.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/setup.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/base.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/bash.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/grep.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/read_file.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/run_command.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/update_file.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/tools/write_file.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/types.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/completers.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/console.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/constants.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/decorators.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/input.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/keybindings.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/lexers.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/output.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/panels.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/prompt_manager.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/tool_ui.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/ui/validators.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/__init__.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/bm25.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/diff_utils.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/file_utils.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/import_cache.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/ripgrep.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/system.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/text_utils.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode/utils/user_configuration.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/SOURCES.txt +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/src/tunacode_cli.egg-info/top_level.txt +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_agent_initialization.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_background_manager.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_config_setup_async.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_file_reference_expansion.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_json_tool_parsing.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_orchestrator_file_references.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/tests/test_orchestrator_import.py +0 -0
- {tunacode_cli-0.0.23 → tunacode_cli-0.0.24}/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.
|
|
3
|
+
Version: 0.0.24
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,6 +30,7 @@ Requires-Dist: black; extra == "dev"
|
|
|
30
30
|
Requires-Dist: flake8; extra == "dev"
|
|
31
31
|
Requires-Dist: isort; extra == "dev"
|
|
32
32
|
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
33
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
35
|
Requires-Dist: textual-dev; extra == "dev"
|
|
35
36
|
Dynamic: license-file
|
|
@@ -131,6 +132,24 @@ Dynamic: license-file
|
|
|
131
132
|
pip install tunacode-cli
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
#### Development Installation
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Clone the repository
|
|
139
|
+
git clone https://github.com/larock22/tunacode.git
|
|
140
|
+
cd tunacode
|
|
141
|
+
|
|
142
|
+
# Run the setup script
|
|
143
|
+
./scripts/setup_dev_env.sh
|
|
144
|
+
|
|
145
|
+
# Or manually:
|
|
146
|
+
python3 -m venv venv
|
|
147
|
+
source venv/bin/activate
|
|
148
|
+
pip install -e ".[dev]"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
|
152
|
+
|
|
134
153
|
#### One-line Install (Linux/macOS)
|
|
135
154
|
|
|
136
155
|
```bash
|
|
@@ -95,6 +95,24 @@
|
|
|
95
95
|
pip install tunacode-cli
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
#### Development Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Clone the repository
|
|
102
|
+
git clone https://github.com/larock22/tunacode.git
|
|
103
|
+
cd tunacode
|
|
104
|
+
|
|
105
|
+
# Run the setup script
|
|
106
|
+
./scripts/setup_dev_env.sh
|
|
107
|
+
|
|
108
|
+
# Or manually:
|
|
109
|
+
python3 -m venv venv
|
|
110
|
+
source venv/bin/activate
|
|
111
|
+
pip install -e ".[dev]"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
|
115
|
+
|
|
98
116
|
#### One-line Install (Linux/macOS)
|
|
99
117
|
|
|
100
118
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tunacode-cli"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.24"
|
|
8
8
|
description = "Your agentic CLI developer."
|
|
9
9
|
keywords = ["cli", "agent", "development", "automation"]
|
|
10
10
|
readme = "README.md"
|
|
@@ -42,6 +42,7 @@ dev = [
|
|
|
42
42
|
"flake8",
|
|
43
43
|
"isort",
|
|
44
44
|
"pytest",
|
|
45
|
+
"pytest-asyncio",
|
|
45
46
|
"pytest-cov",
|
|
46
47
|
"textual-dev",
|
|
47
48
|
]
|
|
@@ -14,6 +14,7 @@ from tunacode.core.state import StateManager
|
|
|
14
14
|
from tunacode.setup import setup
|
|
15
15
|
from tunacode.ui import console as ui
|
|
16
16
|
from tunacode.utils.system import check_for_updates
|
|
17
|
+
from tunacode.exceptions import UserAbortError
|
|
17
18
|
|
|
18
19
|
app_settings = ApplicationSettings()
|
|
19
20
|
app = typer.Typer(help="🐟 TunaCode - Your AI-powered development assistant")
|
|
@@ -49,6 +50,9 @@ def main(
|
|
|
49
50
|
try:
|
|
50
51
|
await setup(run_setup, state_manager, cli_config)
|
|
51
52
|
await repl(state_manager)
|
|
53
|
+
except (KeyboardInterrupt, UserAbortError):
|
|
54
|
+
update_task.cancel()
|
|
55
|
+
return
|
|
52
56
|
except Exception as e:
|
|
53
57
|
from tunacode.exceptions import ConfigurationError
|
|
54
58
|
|
|
@@ -282,6 +282,7 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
|
|
|
282
282
|
|
|
283
283
|
async def repl(state_manager: StateManager):
|
|
284
284
|
action = None
|
|
285
|
+
ctrl_c_pressed = False
|
|
285
286
|
|
|
286
287
|
# Professional startup information
|
|
287
288
|
await ui.muted(f"• Model: {state_manager.session.current_model}")
|
|
@@ -294,12 +295,18 @@ async def repl(state_manager: StateManager):
|
|
|
294
295
|
while True:
|
|
295
296
|
try:
|
|
296
297
|
line = await ui.multiline_input(state_manager, _command_registry)
|
|
297
|
-
except
|
|
298
|
-
|
|
298
|
+
except UserAbortError:
|
|
299
|
+
if ctrl_c_pressed:
|
|
300
|
+
break
|
|
301
|
+
ctrl_c_pressed = True
|
|
302
|
+
await ui.warning("Hit Ctrl+C again to exit")
|
|
303
|
+
continue
|
|
299
304
|
|
|
300
305
|
if not line:
|
|
301
306
|
continue
|
|
302
307
|
|
|
308
|
+
ctrl_c_pressed = False
|
|
309
|
+
|
|
303
310
|
if line.lower() in ["exit", "quit"]:
|
|
304
311
|
break
|
|
305
312
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunacode-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.24
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,6 +30,7 @@ Requires-Dist: black; extra == "dev"
|
|
|
30
30
|
Requires-Dist: flake8; extra == "dev"
|
|
31
31
|
Requires-Dist: isort; extra == "dev"
|
|
32
32
|
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
33
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
35
|
Requires-Dist: textual-dev; extra == "dev"
|
|
35
36
|
Dynamic: license-file
|
|
@@ -131,6 +132,24 @@ Dynamic: license-file
|
|
|
131
132
|
pip install tunacode-cli
|
|
132
133
|
```
|
|
133
134
|
|
|
135
|
+
#### Development Installation
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Clone the repository
|
|
139
|
+
git clone https://github.com/larock22/tunacode.git
|
|
140
|
+
cd tunacode
|
|
141
|
+
|
|
142
|
+
# Run the setup script
|
|
143
|
+
./scripts/setup_dev_env.sh
|
|
144
|
+
|
|
145
|
+
# Or manually:
|
|
146
|
+
python3 -m venv venv
|
|
147
|
+
source venv/bin/activate
|
|
148
|
+
pip install -e ".[dev]"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup.
|
|
152
|
+
|
|
134
153
|
#### One-line Install (Linux/macOS)
|
|
135
154
|
|
|
136
155
|
```bash
|
|
@@ -4,20 +4,21 @@
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
|
+
import pytest
|
|
7
8
|
|
|
8
9
|
# Add src to path
|
|
9
10
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../src'))
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@pytest.mark.asyncio
|
|
12
14
|
async def test_orchestrator_planning():
|
|
13
15
|
"""Test that orchestrator actually creates and shows a plan."""
|
|
14
|
-
print("\n[TEST] Testing orchestrator planning output...")
|
|
15
|
-
|
|
16
16
|
try:
|
|
17
17
|
from tunacode.core.agents.orchestrator import OrchestratorAgent
|
|
18
18
|
from tunacode.core.state import StateManager
|
|
19
19
|
from tunacode.types import ModelName
|
|
20
|
-
from unittest.mock import patch, MagicMock
|
|
20
|
+
from unittest.mock import patch, MagicMock, AsyncMock
|
|
21
|
+
from tunacode.core.agents.planner_schema import Task
|
|
21
22
|
|
|
22
23
|
# Create state manager
|
|
23
24
|
state = StateManager()
|
|
@@ -32,10 +33,10 @@ async def test_orchestrator_planning():
|
|
|
32
33
|
# Create orchestrator
|
|
33
34
|
orchestrator = OrchestratorAgent(state)
|
|
34
35
|
|
|
35
|
-
#
|
|
36
|
+
# Create proper Task objects
|
|
36
37
|
mock_tasks = [
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
Task(id=1, description="Read the file", mutate=False),
|
|
39
|
+
Task(id=2, description="Update the code", mutate=True)
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
# Capture console output
|
|
@@ -45,35 +46,33 @@ async def test_orchestrator_planning():
|
|
|
45
46
|
if args:
|
|
46
47
|
outputs.append(str(args[0]))
|
|
47
48
|
|
|
49
|
+
# Mock the Agent class to avoid API key requirement
|
|
50
|
+
mock_agent_instance = MagicMock()
|
|
51
|
+
mock_agent_instance.run = AsyncMock(return_value=MagicMock(data=mock_tasks))
|
|
52
|
+
|
|
48
53
|
with patch('rich.console.Console.print', side_effect=capture_print):
|
|
49
|
-
|
|
54
|
+
# Mock get_agent_tool to return our mock Agent class
|
|
55
|
+
with patch('tunacode.core.agents.main.get_agent_tool', return_value=(MagicMock(return_value=mock_agent_instance), None)):
|
|
50
56
|
# Mock the agent execution
|
|
51
57
|
mock_run = MagicMock()
|
|
52
58
|
mock_run.result = MagicMock(output="Task completed")
|
|
53
59
|
|
|
54
|
-
with patch.object(orchestrator, '_run_sub_task', return_value=mock_run):
|
|
60
|
+
with patch.object(orchestrator, '_run_sub_task', new_callable=AsyncMock, return_value=mock_run):
|
|
55
61
|
# Run orchestrator
|
|
56
62
|
results = await orchestrator.run("Test request")
|
|
57
63
|
|
|
58
64
|
# Check outputs
|
|
59
|
-
assert any("
|
|
65
|
+
assert any("Orchestrator Mode" in out for out in outputs), "Missing orchestrator start message"
|
|
60
66
|
assert any("Executing plan" in out for out in outputs), "Missing execution message"
|
|
61
|
-
assert any("
|
|
62
|
-
|
|
63
|
-
print("[PASS] Orchestrator displayed planning messages")
|
|
64
|
-
print("[PASS] Orchestrator completed successfully")
|
|
65
|
-
|
|
66
|
-
print("[SUCCESS] Orchestrator planning test PASSED!")
|
|
67
|
+
assert any("Orchestrator completed" in out for out in outputs), "Missing completion message"
|
|
67
68
|
|
|
68
69
|
except ImportError as e:
|
|
69
|
-
|
|
70
|
-
print("[SUCCESS] Integration test skipped (dependencies not available)")
|
|
70
|
+
pytest.skip(f"Skipping integration test due to missing dependencies: {e}")
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
@pytest.mark.asyncio
|
|
73
74
|
async def test_architect_mode_check():
|
|
74
75
|
"""Test the actual check in repl.py for architect mode."""
|
|
75
|
-
print("\n[TEST] Testing architect mode check in process_request...")
|
|
76
|
-
|
|
77
76
|
# Simulate the check from repl.py
|
|
78
77
|
class MockSession:
|
|
79
78
|
architect_mode = False
|
|
@@ -85,36 +84,13 @@ async def test_architect_mode_check():
|
|
|
85
84
|
|
|
86
85
|
# Test the actual condition used in repl.py
|
|
87
86
|
if getattr(state.session, 'architect_mode', False):
|
|
88
|
-
|
|
89
|
-
assert False
|
|
90
|
-
else:
|
|
91
|
-
print("[PASS] Correctly skipping orchestrator when architect_mode is False")
|
|
87
|
+
assert False, "Should not use orchestrator when architect_mode is False"
|
|
92
88
|
|
|
93
89
|
# Enable architect mode
|
|
94
90
|
state.session.architect_mode = True
|
|
95
91
|
|
|
96
|
-
if getattr(state.session, 'architect_mode', False):
|
|
97
|
-
|
|
98
|
-
else:
|
|
99
|
-
print("[FAIL] Should use orchestrator when architect_mode is True")
|
|
100
|
-
assert False
|
|
101
|
-
|
|
102
|
-
print("[SUCCESS] Architect mode check test PASSED!")
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
async def main():
|
|
106
|
-
"""Run integration tests."""
|
|
107
|
-
print("=" * 60)
|
|
108
|
-
print("ARCHITECT MODE INTEGRATION TESTS")
|
|
109
|
-
print("=" * 60)
|
|
110
|
-
|
|
111
|
-
await test_architect_mode_check()
|
|
112
|
-
await test_orchestrator_planning()
|
|
113
|
-
|
|
114
|
-
print("\n" + "=" * 60)
|
|
115
|
-
print("[SUCCESS] ALL INTEGRATION TESTS COMPLETED!")
|
|
116
|
-
print("=" * 60)
|
|
92
|
+
if not getattr(state.session, 'architect_mode', False):
|
|
93
|
+
assert False, "Should use orchestrator when architect_mode is True"
|
|
117
94
|
|
|
118
95
|
|
|
119
|
-
if __name__
|
|
120
|
-
asyncio.run(main())
|
|
96
|
+
# Remove the main() function and if __name__ block since pytest will handle test discovery and execution
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
|
+
import pytest
|
|
7
8
|
|
|
8
9
|
# Add src to path
|
|
9
10
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../src'))
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@pytest.mark.asyncio
|
|
12
14
|
async def test_architect_toggle():
|
|
13
15
|
"""Test that architect mode can be toggled."""
|
|
14
|
-
print("\n[TEST] Testing architect mode toggle...")
|
|
15
|
-
|
|
16
16
|
# Create a minimal state object
|
|
17
17
|
class MockSession:
|
|
18
18
|
def __init__(self):
|
|
@@ -26,25 +26,19 @@ async def test_architect_toggle():
|
|
|
26
26
|
|
|
27
27
|
# Test initial state
|
|
28
28
|
assert not hasattr(state.session, 'architect_mode') or state.session.architect_mode is False
|
|
29
|
-
print("[PASS] Initial state: architect_mode is OFF")
|
|
30
29
|
|
|
31
30
|
# Toggle ON
|
|
32
31
|
state.session.architect_mode = not getattr(state.session, 'architect_mode', False)
|
|
33
32
|
assert state.session.architect_mode is True
|
|
34
|
-
print("[PASS] After toggle: architect_mode is ON")
|
|
35
33
|
|
|
36
34
|
# Toggle OFF
|
|
37
35
|
state.session.architect_mode = not state.session.architect_mode
|
|
38
36
|
assert state.session.architect_mode is False
|
|
39
|
-
print("[PASS] After second toggle: architect_mode is OFF")
|
|
40
|
-
|
|
41
|
-
print("[SUCCESS] Architect toggle test PASSED!")
|
|
42
37
|
|
|
43
38
|
|
|
39
|
+
@pytest.mark.asyncio
|
|
44
40
|
async def test_orchestrator_routing():
|
|
45
41
|
"""Test that process_request routes to orchestrator when architect_mode is ON."""
|
|
46
|
-
print("\n[TEST] Testing orchestrator routing...")
|
|
47
|
-
|
|
48
42
|
# Test the routing logic without actual imports
|
|
49
43
|
class MockSession:
|
|
50
44
|
def __init__(self):
|
|
@@ -59,28 +53,21 @@ async def test_orchestrator_routing():
|
|
|
59
53
|
|
|
60
54
|
# Test routing decision
|
|
61
55
|
if getattr(state.session, 'architect_mode', False):
|
|
62
|
-
print("[PASS] Architect mode ON - Would use orchestrator")
|
|
63
56
|
assert state.session.architect_mode is True
|
|
64
57
|
else:
|
|
65
|
-
print("[FAIL] Architect mode OFF - Would use normal agent")
|
|
66
58
|
assert False, "Should have used orchestrator"
|
|
67
59
|
|
|
68
60
|
# Test with architect_mode OFF
|
|
69
61
|
state.session.architect_mode = False
|
|
70
62
|
if getattr(state.session, 'architect_mode', False):
|
|
71
|
-
print("[FAIL] Architect mode ON - Would use orchestrator")
|
|
72
63
|
assert False, "Should have used normal agent"
|
|
73
64
|
else:
|
|
74
|
-
print("[PASS] Architect mode OFF - Would use normal agent")
|
|
75
65
|
assert state.session.architect_mode is False
|
|
76
|
-
|
|
77
|
-
print("[SUCCESS] Orchestrator routing test PASSED!")
|
|
78
66
|
|
|
79
67
|
|
|
68
|
+
@pytest.mark.asyncio
|
|
80
69
|
async def test_command_parsing():
|
|
81
70
|
"""Test architect command argument parsing."""
|
|
82
|
-
print("\n[TEST] Testing architect command parsing...")
|
|
83
|
-
|
|
84
71
|
# Test various command inputs
|
|
85
72
|
test_cases = [
|
|
86
73
|
(["on"], True, "Explicit ON"),
|
|
@@ -101,25 +88,6 @@ async def test_command_parsing():
|
|
|
101
88
|
result = None
|
|
102
89
|
|
|
103
90
|
assert result == expected, f"Failed for {desc}"
|
|
104
|
-
print(f"[PASS] {desc}: correctly parsed as {expected}")
|
|
105
|
-
|
|
106
|
-
print("[SUCCESS] Command parsing test PASSED!")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
async def main():
|
|
110
|
-
"""Run all tests."""
|
|
111
|
-
print("=" * 60)
|
|
112
|
-
print("ARCHITECT MODE TESTS")
|
|
113
|
-
print("=" * 60)
|
|
114
|
-
|
|
115
|
-
await test_architect_toggle()
|
|
116
|
-
await test_orchestrator_routing()
|
|
117
|
-
await test_command_parsing()
|
|
118
|
-
|
|
119
|
-
print("\n" + "=" * 60)
|
|
120
|
-
print("[SUCCESS] ALL TESTS PASSED!")
|
|
121
|
-
print("=" * 60)
|
|
122
91
|
|
|
123
92
|
|
|
124
|
-
if __name__
|
|
125
|
-
asyncio.run(main())
|
|
93
|
+
# Remove the main() function and if __name__ block since pytest will handle test discovery and execution
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Simple test for fast-glob prefilter search functionality
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
# Add src to path so we can import tunacode modules
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
|
14
|
+
|
|
15
|
+
def test_fast_glob_import():
|
|
16
|
+
"""Test that fast_glob function can be imported"""
|
|
17
|
+
from tunacode.tools.grep import fast_glob
|
|
18
|
+
assert fast_glob is not None
|
|
19
|
+
|
|
20
|
+
def test_fast_glob_basic_functionality():
|
|
21
|
+
"""Test basic fast_glob functionality with real files"""
|
|
22
|
+
from tunacode.tools.grep import fast_glob
|
|
23
|
+
|
|
24
|
+
# Use current directory which has Python files
|
|
25
|
+
root = Path(".")
|
|
26
|
+
|
|
27
|
+
# Test finding Python files
|
|
28
|
+
python_files = fast_glob(root, "*.py")
|
|
29
|
+
|
|
30
|
+
assert len(python_files) > 0, "Should find at least some Python files"
|
|
31
|
+
assert all(str(f).endswith('.py') for f in python_files), "All results should be .py files"
|
|
32
|
+
|
|
33
|
+
# Test specific pattern
|
|
34
|
+
test_files = fast_glob(root, "test_*.py")
|
|
35
|
+
assert len(test_files) >= 2, "Should find our test files" # At least test_react_thoughts.py and this file
|
|
36
|
+
|
|
37
|
+
def test_fast_glob_multiple_extensions():
|
|
38
|
+
"""Test fast_glob with multiple extensions pattern"""
|
|
39
|
+
from tunacode.tools.grep import fast_glob
|
|
40
|
+
|
|
41
|
+
root = Path(".")
|
|
42
|
+
|
|
43
|
+
# Test multiple extensions pattern
|
|
44
|
+
code_files = fast_glob(root, "*.{py,md}")
|
|
45
|
+
|
|
46
|
+
assert len(code_files) > 0, "Should find Python and Markdown files"
|
|
47
|
+
|
|
48
|
+
py_files = [f for f in code_files if str(f).endswith('.py')]
|
|
49
|
+
md_files = [f for f in code_files if str(f).endswith('.md')]
|
|
50
|
+
|
|
51
|
+
assert len(py_files) > 0, "Should find some Python files"
|
|
52
|
+
assert len(md_files) > 0, "Should find some Markdown files"
|
|
53
|
+
|
|
54
|
+
def test_parallel_grep_import():
|
|
55
|
+
"""Test that ParallelGrep class can be imported"""
|
|
56
|
+
from tunacode.tools.grep import ParallelGrep, grep
|
|
57
|
+
assert ParallelGrep is not None
|
|
58
|
+
assert grep is not None
|
|
59
|
+
|
|
60
|
+
@pytest.mark.asyncio
|
|
61
|
+
async def test_grep_search_integration():
|
|
62
|
+
"""Test that grep function works with fast-glob prefilter"""
|
|
63
|
+
from tunacode.tools.grep import grep
|
|
64
|
+
|
|
65
|
+
# Test searching for a pattern we know exists
|
|
66
|
+
result = await grep("import", ".", include_files="*.py", max_results=5)
|
|
67
|
+
|
|
68
|
+
assert isinstance(result, str), "grep should return a string"
|
|
69
|
+
assert "Found" in result or "No matches" in result, "Result should indicate search status"
|
|
70
|
+
|
|
71
|
+
# If we found matches, check they contain our search pattern
|
|
72
|
+
if "Found" in result:
|
|
73
|
+
assert "Strategy:" in result, "Result should show which strategy was used"
|
|
74
|
+
assert "Candidates:" in result, "Result should show candidate count"
|
|
75
|
+
|
|
76
|
+
@pytest.mark.asyncio
|
|
77
|
+
async def test_smart_strategy_selection():
|
|
78
|
+
"""Test that smart strategy selection works based on candidate count"""
|
|
79
|
+
from tunacode.tools.grep import ParallelGrep
|
|
80
|
+
|
|
81
|
+
# Create grep tool instance
|
|
82
|
+
grep_tool = ParallelGrep()
|
|
83
|
+
|
|
84
|
+
# Test with very specific pattern (should find few files)
|
|
85
|
+
result_few = await grep_tool._execute(
|
|
86
|
+
"test_", ".", include_files="test_*.py", search_type="smart", max_results=10
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Should use python strategy for small sets
|
|
90
|
+
assert "Strategy: python" in result_few, "Should use python strategy for small candidate sets"
|
|
91
|
+
|
|
92
|
+
# Test with broader pattern (more files)
|
|
93
|
+
result_many = await grep_tool._execute(
|
|
94
|
+
"import", ".", include_files="*.py", search_type="smart", max_results=10
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Should show some strategy was selected
|
|
98
|
+
assert "Strategy:" in result_many, "Should show strategy selection"
|
|
99
|
+
|
|
100
|
+
def test_bounded_results():
|
|
101
|
+
"""Test that results are properly bounded by MAX_GLOB"""
|
|
102
|
+
from tunacode.tools.grep import fast_glob, MAX_GLOB
|
|
103
|
+
|
|
104
|
+
root = Path(".")
|
|
105
|
+
|
|
106
|
+
# Test that we don't exceed MAX_GLOB even with broad pattern
|
|
107
|
+
all_files = fast_glob(root, "*")
|
|
108
|
+
|
|
109
|
+
assert len(all_files) <= MAX_GLOB, f"Results should be bounded by MAX_GLOB ({MAX_GLOB})"
|
|
110
|
+
|
|
111
|
+
# Remove the main() function and if __name__ block since pytest will handle test discovery and execution
|
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
|
+
import pytest
|
|
7
8
|
|
|
8
9
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../src'))
|
|
9
10
|
|
|
11
|
+
@pytest.mark.asyncio
|
|
10
12
|
async def test_planning_visibility():
|
|
11
13
|
"""Test that planning steps are visible in orchestrator output."""
|
|
12
14
|
from tunacode.core.agents.orchestrator import OrchestratorAgent
|
|
13
15
|
from tunacode.core.state import StateManager
|
|
14
16
|
from tunacode.types import ModelName
|
|
17
|
+
from unittest.mock import patch, MagicMock, AsyncMock
|
|
18
|
+
from tunacode.core.agents.planner_schema import Task
|
|
15
19
|
|
|
16
20
|
# Create state manager
|
|
17
21
|
state = StateManager()
|
|
@@ -37,20 +41,24 @@ async def test_planning_visibility():
|
|
|
37
41
|
Then create a new file called feature.py with a simple function.
|
|
38
42
|
"""
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
# Create proper Task objects
|
|
45
|
+
mock_tasks = [
|
|
46
|
+
Task(id=1, description="Read README.md", mutate=False),
|
|
47
|
+
Task(id=2, description="Create feature.py", mutate=True)
|
|
48
|
+
]
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
# Mock the Agent class to avoid API key requirement
|
|
51
|
+
mock_agent_instance = MagicMock()
|
|
52
|
+
mock_agent_instance.run = AsyncMock(return_value=MagicMock(data=mock_tasks))
|
|
53
|
+
|
|
54
|
+
# Mock get_agent_tool to return our mock Agent class
|
|
55
|
+
with patch('tunacode.core.agents.main.get_agent_tool', return_value=(MagicMock(return_value=mock_agent_instance), None)):
|
|
56
|
+
# Mock the agent execution
|
|
57
|
+
mock_run = MagicMock()
|
|
58
|
+
mock_run.result = MagicMock(output="Task completed")
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if __name__ == "__main__":
|
|
56
|
-
asyncio.run(test_planning_visibility())
|
|
60
|
+
with patch.object(orchestrator, '_run_sub_task', new_callable=AsyncMock, return_value=mock_run):
|
|
61
|
+
# Run the orchestrator
|
|
62
|
+
results = await orchestrator.run(test_request)
|
|
63
|
+
|
|
64
|
+
assert len(results) == len(mock_tasks), f"Expected {len(mock_tasks)} results, got {len(results)}"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Simple test for ReAct thoughts functionality
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
# Add src to path so we can import tunacode modules
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
|
12
|
+
|
|
13
|
+
def test_thoughts_command_import():
|
|
14
|
+
"""Test that ThoughtsCommand can be imported without errors"""
|
|
15
|
+
from tunacode.cli.commands import ThoughtsCommand
|
|
16
|
+
assert ThoughtsCommand is not None
|
|
17
|
+
|
|
18
|
+
def test_session_state_thoughts():
|
|
19
|
+
"""Test that SessionState has show_thoughts attribute"""
|
|
20
|
+
from tunacode.core.state import SessionState
|
|
21
|
+
|
|
22
|
+
# Create a session state
|
|
23
|
+
session = SessionState()
|
|
24
|
+
|
|
25
|
+
# Check default value
|
|
26
|
+
assert hasattr(session, 'show_thoughts'), "SessionState missing show_thoughts attribute"
|
|
27
|
+
assert session.show_thoughts == False, "Default show_thoughts should be False"
|
|
28
|
+
|
|
29
|
+
# Test setting the value
|
|
30
|
+
session.show_thoughts = True
|
|
31
|
+
assert session.show_thoughts == True, "show_thoughts should be settable to True"
|
|
32
|
+
|
|
33
|
+
session.show_thoughts = False
|
|
34
|
+
assert session.show_thoughts == False, "show_thoughts should be settable to False"
|
|
35
|
+
|
|
36
|
+
@pytest.mark.asyncio
|
|
37
|
+
async def test_agent_thought_processing():
|
|
38
|
+
"""Test that agent can process thought messages"""
|
|
39
|
+
from tunacode.core.agents.main import _process_node
|
|
40
|
+
from tunacode.core.state import StateManager
|
|
41
|
+
|
|
42
|
+
# Create a mock state manager
|
|
43
|
+
state_manager = StateManager()
|
|
44
|
+
|
|
45
|
+
# Create a mock node with thought
|
|
46
|
+
class MockNode:
|
|
47
|
+
def __init__(self, thought_text):
|
|
48
|
+
self.thought = thought_text
|
|
49
|
+
|
|
50
|
+
# Test processing a node with a thought
|
|
51
|
+
node_with_thought = MockNode("This is a test thought")
|
|
52
|
+
|
|
53
|
+
# This should not raise an error
|
|
54
|
+
await _process_node(node_with_thought, None, state_manager)
|
|
55
|
+
|
|
56
|
+
# Check that thought was added to messages
|
|
57
|
+
messages = state_manager.session.messages
|
|
58
|
+
thought_messages = [msg for msg in messages if isinstance(msg, dict) and "thought" in msg]
|
|
59
|
+
|
|
60
|
+
assert len(thought_messages) > 0, "Thought message should be added to session messages"
|
|
61
|
+
assert thought_messages[0]["thought"] == "This is a test thought", "Thought content should match"
|
|
62
|
+
|
|
63
|
+
@pytest.mark.asyncio
|
|
64
|
+
async def test_thoughts_command_functionality():
|
|
65
|
+
"""Test ThoughtsCommand execute method"""
|
|
66
|
+
from tunacode.cli.commands import ThoughtsCommand, CommandContext
|
|
67
|
+
from tunacode.core.state import StateManager
|
|
68
|
+
|
|
69
|
+
# Create command and context
|
|
70
|
+
command = ThoughtsCommand()
|
|
71
|
+
state_manager = StateManager()
|
|
72
|
+
context = CommandContext(state_manager=state_manager)
|
|
73
|
+
|
|
74
|
+
# Test initial state
|
|
75
|
+
assert state_manager.session.show_thoughts == False, "Initial state should be False"
|
|
76
|
+
|
|
77
|
+
# Test toggle with no args (should toggle)
|
|
78
|
+
await command.execute([], context)
|
|
79
|
+
assert state_manager.session.show_thoughts == True, "Should toggle to True"
|
|
80
|
+
|
|
81
|
+
await command.execute([], context)
|
|
82
|
+
assert state_manager.session.show_thoughts == False, "Should toggle back to False"
|
|
83
|
+
|
|
84
|
+
# Test explicit on
|
|
85
|
+
await command.execute(["on"], context)
|
|
86
|
+
assert state_manager.session.show_thoughts == True, "Should set to True with 'on'"
|
|
87
|
+
|
|
88
|
+
# Test explicit off
|
|
89
|
+
await command.execute(["off"], context)
|
|
90
|
+
assert state_manager.session.show_thoughts == False, "Should set to False with 'off'"
|
|
91
|
+
|
|
92
|
+
# Remove the main() function and if __name__ block since pytest will handle test discovery and execution
|