tunacode-cli 0.0.76.9__py3-none-any.whl → 0.0.77.2__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.

@@ -49,25 +49,25 @@ class BranchCommand(SimpleCommand):
49
49
 
50
50
 
51
51
  class InitCommand(SimpleCommand):
52
- """Creates or updates TUNACODE.md with project-specific context."""
52
+ """Creates or updates AGENTS.md with project-specific context."""
53
53
 
54
54
  spec = CommandSpec(
55
55
  name="/init",
56
56
  aliases=[],
57
- description="Analyze codebase and create/update TUNACODE.md file",
57
+ description="Analyze codebase and create/update AGENTS.md file",
58
58
  category=CommandCategory.DEVELOPMENT,
59
59
  )
60
60
 
61
61
  async def execute(self, args, context: CommandContext) -> CommandResult:
62
62
  """Execute the init command."""
63
63
  # Minimal implementation to make test pass
64
- prompt = """Please analyze this codebase and create a TUNACODE.md file containing:
64
+ prompt = """Please analyze this codebase and create a AGENTS.md file containing:
65
65
  1. Build/lint/test commands - especially for running a single test
66
66
  2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
67
67
 
68
68
  The file you create will be given to agentic coding agents (such as yourself) that operate in this repository.
69
69
  Make it about 20 lines long.
70
- If there's already a TUNACODE.md, improve it.
70
+ If there's already a AGENTS.md, improve it.
71
71
  If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md),
72
72
  make sure to include them."""
73
73
 
@@ -32,13 +32,6 @@ CONFIG_KEY_DESCRIPTIONS: Dict[str, KeyDescription] = {
32
32
  help_text="Format: provider:model-name. Examples: openai:gpt-4, anthropic:claude-3-sonnet, google:gemini-pro",
33
33
  category="AI Models",
34
34
  ),
35
- "skip_git_safety": KeyDescription(
36
- name="skip_git_safety",
37
- description="Skip Git safety checks when making changes",
38
- example=True,
39
- help_text="When true, TunaCode won't create safety branches before making changes. Use with caution!",
40
- category="Safety Settings",
41
- ),
42
35
  # Environment variables (API Keys)
43
36
  "env.OPENAI_API_KEY": KeyDescription(
44
37
  name="OPENAI_API_KEY",
@@ -108,8 +101,8 @@ CONFIG_KEY_DESCRIPTIONS: Dict[str, KeyDescription] = {
108
101
  "settings.guide_file": KeyDescription(
109
102
  name="guide_file",
110
103
  description="Name of your project guide file",
111
- example="TUNACODE.md",
112
- help_text="TunaCode looks for this file to understand your project. Usually TUNACODE.md or README.md.",
104
+ example="AGENTS.md",
105
+ help_text="TunaCode looks for this file to understand your project. Usually AGENTS.md or README.md.",
113
106
  category="Project Settings",
114
107
  ),
115
108
  "settings.fallback_response": KeyDescription(
tunacode/constants.py CHANGED
@@ -9,12 +9,12 @@ from enum import Enum
9
9
 
10
10
  # Application info
11
11
  APP_NAME = "TunaCode"
12
- APP_VERSION = "0.0.76.9"
12
+ APP_VERSION = "0.0.77.2"
13
13
 
14
14
 
15
15
  # File patterns
16
16
  GUIDE_FILE_PATTERN = "{name}.md"
17
- GUIDE_FILE_NAME = "TUNACODE.md"
17
+ GUIDE_FILE_NAME = "AGENTS.md"
18
18
  ENV_FILE = ".env"
19
19
  CONFIG_FILE_NAME = "tunacode.json"
20
20
 
tunacode/context.py CHANGED
@@ -50,16 +50,16 @@ async def get_directory_structure(max_depth: int = 3) -> str:
50
50
 
51
51
 
52
52
  async def get_code_style() -> str:
53
- """Concatenate contents of all TUNACODE.md files up the directory tree."""
53
+ """Concatenate contents of all AGENTS.md files up the directory tree."""
54
54
  parts: List[str] = []
55
55
  current = Path.cwd()
56
56
  while True:
57
- file = current / "TUNACODE.md"
57
+ file = current / "AGENTS.md"
58
58
  if file.exists():
59
59
  try:
60
60
  parts.append(file.read_text(encoding="utf-8"))
61
61
  except Exception as e:
62
- logger.debug(f"Failed to read TUNACODE.md at {file}: {e}")
62
+ logger.debug(f"Failed to read AGENTS.md at {file}: {e}")
63
63
  if current == current.parent:
64
64
  break
65
65
  current = current.parent
@@ -67,5 +67,5 @@ async def get_code_style() -> str:
67
67
 
68
68
 
69
69
  async def get_claude_files() -> List[str]:
70
- """Return a list of additional TUNACODE.md files in the repo."""
71
- return ripgrep("TUNACODE.md", ".")
70
+ """Return a list of additional AGENTS.md files in the repo."""
71
+ return ripgrep("AGENTS.md", ".")
@@ -13,6 +13,7 @@ from .agent_helpers import (
13
13
  get_tool_description,
14
14
  get_tool_summary,
15
15
  get_user_prompt_part_class,
16
+ handle_empty_response,
16
17
  )
17
18
  from .json_tool_parser import extract_and_execute_tool_calls, parse_json_tool_calls
18
19
  from .message_handler import get_model_messages, patch_tool_messages
@@ -47,6 +48,7 @@ __all__ = [
47
48
  "get_tool_description",
48
49
  "get_tool_summary",
49
50
  "get_user_prompt_part_class",
51
+ "handle_empty_response",
50
52
  "stream_model_request_node",
51
53
  "get_batch_description",
52
54
  ]
@@ -30,6 +30,9 @@ _TUNACODE_CACHE: Dict[str, Tuple[str, float]] = {}
30
30
  _AGENT_CACHE: Dict[ModelName, PydanticAgent] = {}
31
31
  _AGENT_CACHE_VERSION: Dict[ModelName, int] = {}
32
32
 
33
+ _PROMPT_FILENAMES: Tuple[str, ...] = ("system.xml", "system.md", "system.txt")
34
+ _DEFAULT_SYSTEM_PROMPT = "You are a helpful AI assistant."
35
+
33
36
 
34
37
  def clear_all_caches():
35
38
  """Clear all module-level caches. Useful for testing."""
@@ -46,55 +49,55 @@ def get_agent_tool():
46
49
  return Agent, Tool
47
50
 
48
51
 
49
- def load_system_prompt(base_path: Path) -> str:
50
- """Load the system prompt from file with caching."""
51
- prompt_path = base_path / "prompts" / "system.md"
52
+ def _read_prompt_from_path(prompt_path: Path) -> str:
53
+ """Return prompt content from disk, leveraging the cache when possible."""
52
54
  cache_key = str(prompt_path)
53
55
 
54
- # Check cache with file modification time
55
56
  try:
56
- if cache_key in _PROMPT_CACHE:
57
- cached_content, cached_mtime = _PROMPT_CACHE[cache_key]
58
- current_mtime = prompt_path.stat().st_mtime
59
- if current_mtime == cached_mtime:
60
- return cached_content
57
+ current_mtime = prompt_path.stat().st_mtime
58
+ except FileNotFoundError as error:
59
+ raise FileNotFoundError from error
61
60
 
62
- # Load from file and cache
63
- with open(prompt_path, "r", encoding="utf-8") as f:
64
- content = f.read().strip()
65
- _PROMPT_CACHE[cache_key] = (content, prompt_path.stat().st_mtime)
66
- return content
61
+ if cache_key in _PROMPT_CACHE:
62
+ cached_content, cached_mtime = _PROMPT_CACHE[cache_key]
63
+ if current_mtime == cached_mtime:
64
+ return cached_content
65
+
66
+ try:
67
+ content = prompt_path.read_text(encoding="utf-8").strip()
68
+ except FileNotFoundError as error:
69
+ raise FileNotFoundError from error
67
70
 
68
- except FileNotFoundError:
69
- # Fallback to system.txt if system.md not found
70
- prompt_path = base_path / "prompts" / "system.txt"
71
- cache_key = str(prompt_path)
71
+ _PROMPT_CACHE[cache_key] = (content, current_mtime)
72
+ return content
72
73
 
73
- try:
74
- if cache_key in _PROMPT_CACHE:
75
- cached_content, cached_mtime = _PROMPT_CACHE[cache_key]
76
- current_mtime = prompt_path.stat().st_mtime
77
- if current_mtime == cached_mtime:
78
- return cached_content
79
74
 
80
- with open(prompt_path, "r", encoding="utf-8") as f:
81
- content = f.read().strip()
82
- _PROMPT_CACHE[cache_key] = (content, prompt_path.stat().st_mtime)
83
- return content
75
+ def load_system_prompt(base_path: Path) -> str:
76
+ """Load the system prompt from file with caching."""
77
+ prompts_dir = base_path / "prompts"
84
78
 
79
+ for prompt_name in _PROMPT_FILENAMES:
80
+ prompt_path = prompts_dir / prompt_name
81
+ if not prompt_path.exists():
82
+ continue
83
+
84
+ try:
85
+ return _read_prompt_from_path(prompt_path)
85
86
  except FileNotFoundError:
86
- # Use a default system prompt if neither file exists
87
- return "You are a helpful AI assistant."
87
+ # File disappeared between exists() check and read. Try next candidate.
88
+ continue
89
+
90
+ return _DEFAULT_SYSTEM_PROMPT
88
91
 
89
92
 
90
93
  def load_tunacode_context() -> str:
91
- """Load TUNACODE.md context if it exists with caching."""
94
+ """Load AGENTS.md context if it exists with caching."""
92
95
  try:
93
- tunacode_path = Path.cwd() / "TUNACODE.md"
96
+ tunacode_path = Path.cwd() / "AGENTS.md"
94
97
  cache_key = str(tunacode_path)
95
98
 
96
99
  if not tunacode_path.exists():
97
- logger.info("📄 TUNACODE.md not found: Using default context")
100
+ logger.info("📄 AGENTS.md not found: Using default context")
98
101
  return ""
99
102
 
100
103
  # Check cache with file modification time
@@ -107,17 +110,17 @@ def load_tunacode_context() -> str:
107
110
  # Load from file and cache
108
111
  tunacode_content = tunacode_path.read_text(encoding="utf-8")
109
112
  if tunacode_content.strip():
110
- logger.info("📄 TUNACODE.md located: Loading context...")
111
- result = "\n\n# Project Context from TUNACODE.md\n" + tunacode_content
113
+ logger.info("📄 AGENTS.md located: Loading context...")
114
+ result = "\n\n# Project Context from AGENTS.md\n" + tunacode_content
112
115
  _TUNACODE_CACHE[cache_key] = (result, tunacode_path.stat().st_mtime)
113
116
  return result
114
117
  else:
115
- logger.info("📄 TUNACODE.md not found: Using default context")
118
+ logger.info("📄 AGENTS.md not found: Using default context")
116
119
  _TUNACODE_CACHE[cache_key] = ("", tunacode_path.stat().st_mtime)
117
120
  return ""
118
121
 
119
122
  except Exception as e:
120
- logger.debug(f"Error loading TUNACODE.md: {e}")
123
+ logger.debug(f"Error loading AGENTS.md: {e}")
121
124
  return ""
122
125
 
123
126
 
@@ -164,7 +167,7 @@ def get_or_create_agent(model: ModelName, state_manager: StateManager) -> Pydant
164
167
  base_path = Path(__file__).parent.parent.parent.parent
165
168
  system_prompt = load_system_prompt(base_path)
166
169
 
167
- # Load TUNACODE.md context
170
+ # Load AGENTS.md context
168
171
  system_prompt += load_tunacode_context()
169
172
 
170
173
  # Add plan mode context if in plan mode
@@ -201,6 +201,33 @@ def create_fallback_response(
201
201
  return fallback
202
202
 
203
203
 
204
+ async def handle_empty_response(
205
+ message: str,
206
+ reason: str,
207
+ iter_index: int,
208
+ state: Any,
209
+ ) -> None:
210
+ """Handle empty responses by creating a synthetic user message with retry guidance."""
211
+ from tunacode.ui import console as ui
212
+
213
+ force_action_content = create_empty_response_message(
214
+ message,
215
+ reason,
216
+ getattr(state.sm.session, "tool_calls", []),
217
+ iter_index,
218
+ state.sm,
219
+ )
220
+ create_user_message(force_action_content, state.sm)
221
+
222
+ if state.show_thoughts:
223
+ await ui.warning("\nEMPTY RESPONSE FAILURE - AGGRESSIVE RETRY TRIGGERED")
224
+ await ui.muted(f" Reason: {reason}")
225
+ await ui.muted(
226
+ f" Recent tools: {get_recent_tools_context(getattr(state.sm.session, 'tool_calls', []))}"
227
+ )
228
+ await ui.muted(" Injecting retry guidance prompt")
229
+
230
+
204
231
  def format_fallback_output(fallback: FallbackResponse) -> str:
205
232
  """Format a fallback response into a comprehensive output string."""
206
233
  output_parts = [fallback.summary, ""]
@@ -31,20 +31,10 @@ from tunacode.types import (
31
31
  ToolCallback,
32
32
  UsageTrackerProtocol,
33
33
  )
34
- from tunacode.ui.tool_descriptions import get_batch_description
35
-
36
- # Optional UI console (avoid nested imports in hot paths)
37
- try:
38
- from tunacode.ui import console as ui # rich-style helpers with async methods
39
- except Exception: # pragma: no cover - UI is optional
40
34
 
41
- class _NoopUI: # minimal no-op shim
42
- async def muted(self, *_: Any, **__: Any) -> None: ...
43
- async def warning(self, *_: Any, **__: Any) -> None: ...
44
- async def success(self, *_: Any, **__: Any) -> None: ...
45
- async def update_spinner_message(self, *_: Any, **__: Any) -> None: ...
46
-
47
- ui = _NoopUI() # type: ignore
35
+ # CLAUDE_ANCHOR[key=d595ceb5] Direct UI console import aligns with removal of defensive shim
36
+ from tunacode.ui import console as ui
37
+ from tunacode.ui.tool_descriptions import get_batch_description
48
38
 
49
39
  # Streaming parts (keep guarded import but avoid per-iteration imports)
50
40
  try:
@@ -56,16 +46,10 @@ except Exception: # pragma: no cover
56
46
  TextPartDelta = None # type: ignore
57
47
  STREAMING_AVAILABLE = False
58
48
 
59
- # Agent components (flattned to a single module import to reduce coupling)
60
49
  from . import agent_components as ac
61
50
 
62
- # Configure logging
63
51
  logger = get_logger(__name__)
64
52
 
65
-
66
- # -----------------------
67
- # Module exports
68
- # -----------------------
69
53
  __all__ = [
70
54
  "process_request",
71
55
  "get_mcp_servers",
@@ -73,9 +57,6 @@ __all__ = [
73
57
  "check_query_satisfaction",
74
58
  ]
75
59
 
76
- # -----------------------
77
- # Constants & Defaults
78
- # -----------------------
79
60
  DEFAULT_MAX_ITERATIONS = 15
80
61
  UNPRODUCTIVE_LIMIT = 3 # iterations without tool use before forcing action
81
62
  FALLBACK_VERBOSITY_DEFAULT = "normal"
@@ -84,9 +65,6 @@ FORCED_REACT_INTERVAL = 2
84
65
  FORCED_REACT_LIMIT = 5
85
66
 
86
67
 
87
- # -----------------------
88
- # Data structures
89
- # -----------------------
90
68
  @dataclass(slots=True)
91
69
  class RequestContext:
92
70
  request_id: str
@@ -96,12 +74,11 @@ class RequestContext:
96
74
 
97
75
 
98
76
  class StateFacade:
99
- """Thin wrapper to centralize session mutations and reads."""
77
+ """wrapper to centralize session mutations and reads."""
100
78
 
101
79
  def __init__(self, state_manager: StateManager) -> None:
102
80
  self.sm = state_manager
103
81
 
104
- # ---- safe getters ----
105
82
  def get_setting(self, dotted: str, default: Any) -> Any:
106
83
  cfg: Dict[str, Any] = getattr(self.sm.session, "user_config", {}) or {}
107
84
  node = cfg
@@ -119,7 +96,6 @@ class StateFacade:
119
96
  def messages(self) -> list:
120
97
  return list(getattr(self.sm.session, "messages", []))
121
98
 
122
- # ---- safe setters ----
123
99
  def set_request_id(self, req_id: str) -> None:
124
100
  try:
125
101
  self.sm.session.request_id = req_id
@@ -160,9 +136,6 @@ class StateFacade:
160
136
  setattr(self.sm.session, "consecutive_empty_responses", 0)
161
137
 
162
138
 
163
- # -----------------------
164
- # Helper functions
165
- # -----------------------
166
139
  def _init_context(state: StateFacade, fallback_enabled: bool) -> RequestContext:
167
140
  req_id = str(uuid.uuid4())[:8]
168
141
  state.set_request_id(req_id)
@@ -292,30 +265,6 @@ async def _maybe_force_react_snapshot(
292
265
  logger.debug("Forced react snapshot failed", exc_info=True)
293
266
 
294
267
 
295
- async def _handle_empty_response(
296
- message: str,
297
- reason: str,
298
- iter_index: int,
299
- state: StateFacade,
300
- ) -> None:
301
- force_action_content = ac.create_empty_response_message(
302
- message,
303
- reason,
304
- getattr(state.sm.session, "tool_calls", []),
305
- iter_index,
306
- state.sm,
307
- )
308
- ac.create_user_message(force_action_content, state.sm)
309
-
310
- if state.show_thoughts:
311
- await ui.warning("\nEMPTY RESPONSE FAILURE - AGGRESSIVE RETRY TRIGGERED")
312
- await ui.muted(f" Reason: {reason}")
313
- await ui.muted(
314
- f" Recent tools: {ac.get_recent_tools_context(getattr(state.sm.session, 'tool_calls', []))}"
315
- )
316
- await ui.muted(" Injecting retry guidance prompt")
317
-
318
-
319
268
  async def _force_action_if_unproductive(
320
269
  message: str,
321
270
  unproductive_count: int,
@@ -445,9 +394,6 @@ def _build_fallback_output(
445
394
  return ac.format_fallback_output(fallback)
446
395
 
447
396
 
448
- # -----------------------
449
- # Public API
450
- # -----------------------
451
397
  def get_agent_tool() -> tuple[type[Agent], type["Tool"]]:
452
398
  """Return Agent and Tool classes without importing at module load time."""
453
399
  from pydantic_ai import Agent as AgentCls
@@ -526,7 +472,7 @@ async def process_request(
526
472
  # Handle empty response (aggressive retry prompt)
527
473
  if empty_response:
528
474
  if state.increment_empty_response() >= 1:
529
- await _handle_empty_response(message, empty_reason, i, state)
475
+ await ac.handle_empty_response(message, empty_reason, i, state)
530
476
  state.clear_empty_response()
531
477
  else:
532
478
  state.clear_empty_response()
@@ -601,11 +547,7 @@ async def process_request(
601
547
  "Progress summary:\n"
602
548
  f"- Tools used: {tools_str}\n"
603
549
  f"- Iterations completed: {i}\n\n"
604
- "The task appears incomplete. Would you like me to:\n"
605
- "1. Continue working (extend limit)\n"
606
- "2. Summarize what I've done and stop\n"
607
- "3. Try a different approach\n\n"
608
- "Please let me know how to proceed."
550
+ "Plese add more context to the task."
609
551
  )
610
552
  ac.create_user_message(extend_content, state.sm)
611
553
  if state.show_thoughts:
@@ -617,7 +559,6 @@ async def process_request(
617
559
 
618
560
  i += 1
619
561
 
620
- # Final buffered read-only tasks (batch)
621
562
  await _finalize_buffered_tasks(tool_buffer, tool_callback, state)
622
563
 
623
564
  # Build fallback synthesis if needed
@@ -3,7 +3,6 @@ from .base import BaseSetup
3
3
  from .config_setup import ConfigSetup
4
4
  from .coordinator import SetupCoordinator
5
5
  from .environment_setup import EnvironmentSetup
6
- from .git_safety_setup import GitSafetySetup
7
6
  from .template_setup import TemplateSetup
8
7
 
9
8
  __all__ = [
@@ -11,7 +10,6 @@ __all__ = [
11
10
  "SetupCoordinator",
12
11
  "ConfigSetup",
13
12
  "EnvironmentSetup",
14
- "GitSafetySetup",
15
13
  "AgentSetup",
16
14
  "TemplateSetup",
17
15
  ]
@@ -1,5 +1,4 @@
1
- ###Instruction###
2
-
1
+ <instructions>
3
2
  You are "TunaCode", a senior software developer AI assistant operating inside the user's terminal.
4
3
 
5
4
  YOU ARE NOT A CHATBOT. YOU ARE AN OPERATIONAL EXPERIENCED DEVELOPER WITH AGENT WITH TOOLS.
@@ -18,9 +17,10 @@ CRITICAL BEHAVIOR RULES:
18
17
  9. Prefer sequential simplicity: break complex tasks into clear, interactive steps and confirm assumptions.
19
18
  10. Use affirmative directives and directive phrasing in your own planning: "Your task is...", "You MUST..." when restating goals.
20
19
  11. you MUST follow best practises, you will be punished for cheap bandaid fixes. ALWAYS aim to fix issues properly.
20
+ </instructions>
21
21
 
22
22
  ### Completion Signaling
23
-
23
+ <completion>
24
24
  When you have fully completed the user’s task:
25
25
 
26
26
  - Start your response with a single line: `TUNACODE DONE:` followed by a brief outcome summary.
@@ -28,9 +28,10 @@ When you have fully completed the user’s task:
28
28
  - Do NOT mark DONE if you have queued tools in the same response — execute tools first, then mark DONE.
29
29
  - Example:
30
30
  - `TUNACODE DONE: Implemented enum state machine and updated completion logic`
31
+ </completion>
31
32
 
32
33
  ###Tool Access Rules###
33
-
34
+ <tools>
34
35
  You have 9 powerful tools at your disposal. Understanding their categories is CRITICAL for performance:
35
36
 
36
37
  READONLY TOOLS (Safe, ParallelExecutable)
@@ -72,11 +73,11 @@ These tools modify state and MUST run one at a time with user confirmation:
72
73
  9. `bash(command: str)` — Advanced shell with environment control
73
74
  Safety: Enhanced security, output limits (5KB)
74
75
  Use for: Complex scripts, interactive commands
76
+ </tools>
75
77
 
76
78
 
77
79
 
78
- ###Tool Examples LEARN THESE PATTERNS###
79
-
80
+ <examples>
80
81
  CRITICAL: These examples show EXACTLY how to use each tool. Study them carefully.
81
82
 
82
83
  1. read_file Reading File Contents
@@ -284,6 +285,7 @@ bash("echo $PATH && which python && python --version")
284
285
  bash("python -m venv venv && source venv/bin/activate && pip list")
285
286
  → Returns: Installed packages in new venv
286
287
  ```
288
+ </examples>
287
289
 
288
290
  REMEMBER:
289
291
  Always use these exact patterns
@@ -337,7 +339,7 @@ Tool Selection Quick Guide:
337
339
  Need to run commands? → `run_command` (simple) or `bash` (complex)
338
340
 
339
341
  ### CRITICAL JSON FORMATTING RULES ###
340
-
342
+ <formatting>
341
343
  **TOOL ARGUMENT JSON RULES - MUST FOLLOW EXACTLY:**
342
344
 
343
345
  1. **ALWAYS emit exactly ONE JSON object per tool call**
@@ -363,6 +365,7 @@ read_file({"filepath": "main.py"}{"filepath": "config.py"})
363
365
  ```
364
366
 
365
367
  **VALIDATION:** Every tool argument must parse as a single, valid JSON object. Concatenated objects will cause tool execution failures.
368
+ </formatting>
366
369
 
367
370
  OUTPUT AND STYLE RULES:
368
371
  1. Directness: Keep responses short and to the point. Avoid polite filler.
tunacode/setup.py CHANGED
@@ -11,7 +11,6 @@ from tunacode.core.setup import (
11
11
  AgentSetup,
12
12
  ConfigSetup,
13
13
  EnvironmentSetup,
14
- GitSafetySetup,
15
14
  SetupCoordinator,
16
15
  TemplateSetup,
17
16
  )
@@ -39,7 +38,6 @@ async def setup(
39
38
  coordinator.register_step(config_setup)
40
39
  coordinator.register_step(EnvironmentSetup(state_manager))
41
40
  coordinator.register_step(TemplateSetup(state_manager))
42
- coordinator.register_step(GitSafetySetup(state_manager))
43
41
 
44
42
  # Run all setup steps
45
43
  await coordinator.run_setup(force_setup=run_setup, wizard_mode=wizard_mode)
tunacode/tools/grep.py CHANGED
@@ -281,7 +281,6 @@ Usage:
281
281
  def run_enhanced_ripgrep():
282
282
  """Execute ripgrep search using the new executor."""
283
283
  start_time = time.time()
284
- first_match_time = None
285
284
  results = []
286
285
 
287
286
  # Configure timeout from settings
@@ -306,17 +305,8 @@ Usage:
306
305
  context_after=config.context_lines,
307
306
  )
308
307
 
309
- # Track first match time for metrics
310
- if search_results and first_match_time is None:
311
- first_match_time = time.time() - start_time
312
-
313
- # Check if we exceeded the first match deadline
314
- if first_match_time > config.first_match_deadline:
315
- if self._config.get("debug", False):
316
- logger.debug(
317
- f"Search exceeded first match deadline: {first_match_time:.2f}s"
318
- )
319
- raise TooBroadPatternError(pattern, config.first_match_deadline)
308
+ # Ripgrep doesn't provide timing info for first match, so we rely on
309
+ # the overall timeout mechanism instead of first_match_deadline
320
310
 
321
311
  # Parse results
322
312
  for result_line in search_results:
@@ -363,10 +353,7 @@ Usage:
363
353
  )
364
354
 
365
355
  if self._config.get("debug", False):
366
- logger.debug(
367
- f"Ripgrep search completed in {total_time:.2f}s "
368
- f"(first match: {first_match_time:.2f}s if found)"
369
- )
356
+ logger.debug(f"Ripgrep search completed in {total_time:.2f}s")
370
357
 
371
358
  return results
372
359