tunacode-cli 0.1.21__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.

Files changed (174) hide show
  1. tunacode/__init__.py +0 -0
  2. tunacode/cli/textual_repl.tcss +283 -0
  3. tunacode/configuration/__init__.py +1 -0
  4. tunacode/configuration/defaults.py +45 -0
  5. tunacode/configuration/models.py +147 -0
  6. tunacode/configuration/models_registry.json +1 -0
  7. tunacode/configuration/pricing.py +74 -0
  8. tunacode/configuration/settings.py +35 -0
  9. tunacode/constants.py +227 -0
  10. tunacode/core/__init__.py +6 -0
  11. tunacode/core/agents/__init__.py +39 -0
  12. tunacode/core/agents/agent_components/__init__.py +48 -0
  13. tunacode/core/agents/agent_components/agent_config.py +441 -0
  14. tunacode/core/agents/agent_components/agent_helpers.py +290 -0
  15. tunacode/core/agents/agent_components/message_handler.py +99 -0
  16. tunacode/core/agents/agent_components/node_processor.py +477 -0
  17. tunacode/core/agents/agent_components/response_state.py +129 -0
  18. tunacode/core/agents/agent_components/result_wrapper.py +51 -0
  19. tunacode/core/agents/agent_components/state_transition.py +112 -0
  20. tunacode/core/agents/agent_components/streaming.py +271 -0
  21. tunacode/core/agents/agent_components/task_completion.py +40 -0
  22. tunacode/core/agents/agent_components/tool_buffer.py +44 -0
  23. tunacode/core/agents/agent_components/tool_executor.py +101 -0
  24. tunacode/core/agents/agent_components/truncation_checker.py +37 -0
  25. tunacode/core/agents/delegation_tools.py +109 -0
  26. tunacode/core/agents/main.py +545 -0
  27. tunacode/core/agents/prompts.py +66 -0
  28. tunacode/core/agents/research_agent.py +231 -0
  29. tunacode/core/compaction.py +218 -0
  30. tunacode/core/prompting/__init__.py +27 -0
  31. tunacode/core/prompting/loader.py +66 -0
  32. tunacode/core/prompting/prompting_engine.py +98 -0
  33. tunacode/core/prompting/sections.py +50 -0
  34. tunacode/core/prompting/templates.py +69 -0
  35. tunacode/core/state.py +409 -0
  36. tunacode/exceptions.py +313 -0
  37. tunacode/indexing/__init__.py +5 -0
  38. tunacode/indexing/code_index.py +432 -0
  39. tunacode/indexing/constants.py +86 -0
  40. tunacode/lsp/__init__.py +112 -0
  41. tunacode/lsp/client.py +351 -0
  42. tunacode/lsp/diagnostics.py +19 -0
  43. tunacode/lsp/servers.py +101 -0
  44. tunacode/prompts/default_prompt.md +952 -0
  45. tunacode/prompts/research/sections/agent_role.xml +5 -0
  46. tunacode/prompts/research/sections/constraints.xml +14 -0
  47. tunacode/prompts/research/sections/output_format.xml +57 -0
  48. tunacode/prompts/research/sections/tool_use.xml +23 -0
  49. tunacode/prompts/sections/advanced_patterns.xml +255 -0
  50. tunacode/prompts/sections/agent_role.xml +8 -0
  51. tunacode/prompts/sections/completion.xml +10 -0
  52. tunacode/prompts/sections/critical_rules.xml +37 -0
  53. tunacode/prompts/sections/examples.xml +220 -0
  54. tunacode/prompts/sections/output_style.xml +94 -0
  55. tunacode/prompts/sections/parallel_exec.xml +105 -0
  56. tunacode/prompts/sections/search_pattern.xml +100 -0
  57. tunacode/prompts/sections/system_info.xml +6 -0
  58. tunacode/prompts/sections/tool_use.xml +84 -0
  59. tunacode/prompts/sections/user_instructions.xml +3 -0
  60. tunacode/py.typed +0 -0
  61. tunacode/templates/__init__.py +5 -0
  62. tunacode/templates/loader.py +15 -0
  63. tunacode/tools/__init__.py +10 -0
  64. tunacode/tools/authorization/__init__.py +29 -0
  65. tunacode/tools/authorization/context.py +32 -0
  66. tunacode/tools/authorization/factory.py +20 -0
  67. tunacode/tools/authorization/handler.py +58 -0
  68. tunacode/tools/authorization/notifier.py +35 -0
  69. tunacode/tools/authorization/policy.py +19 -0
  70. tunacode/tools/authorization/requests.py +119 -0
  71. tunacode/tools/authorization/rules.py +72 -0
  72. tunacode/tools/bash.py +222 -0
  73. tunacode/tools/decorators.py +213 -0
  74. tunacode/tools/glob.py +353 -0
  75. tunacode/tools/grep.py +468 -0
  76. tunacode/tools/grep_components/__init__.py +9 -0
  77. tunacode/tools/grep_components/file_filter.py +93 -0
  78. tunacode/tools/grep_components/pattern_matcher.py +158 -0
  79. tunacode/tools/grep_components/result_formatter.py +87 -0
  80. tunacode/tools/grep_components/search_result.py +34 -0
  81. tunacode/tools/list_dir.py +205 -0
  82. tunacode/tools/prompts/bash_prompt.xml +10 -0
  83. tunacode/tools/prompts/glob_prompt.xml +7 -0
  84. tunacode/tools/prompts/grep_prompt.xml +10 -0
  85. tunacode/tools/prompts/list_dir_prompt.xml +7 -0
  86. tunacode/tools/prompts/read_file_prompt.xml +9 -0
  87. tunacode/tools/prompts/todoclear_prompt.xml +12 -0
  88. tunacode/tools/prompts/todoread_prompt.xml +16 -0
  89. tunacode/tools/prompts/todowrite_prompt.xml +28 -0
  90. tunacode/tools/prompts/update_file_prompt.xml +9 -0
  91. tunacode/tools/prompts/web_fetch_prompt.xml +11 -0
  92. tunacode/tools/prompts/write_file_prompt.xml +7 -0
  93. tunacode/tools/react.py +111 -0
  94. tunacode/tools/read_file.py +68 -0
  95. tunacode/tools/todo.py +222 -0
  96. tunacode/tools/update_file.py +62 -0
  97. tunacode/tools/utils/__init__.py +1 -0
  98. tunacode/tools/utils/ripgrep.py +311 -0
  99. tunacode/tools/utils/text_match.py +352 -0
  100. tunacode/tools/web_fetch.py +245 -0
  101. tunacode/tools/write_file.py +34 -0
  102. tunacode/tools/xml_helper.py +34 -0
  103. tunacode/types/__init__.py +166 -0
  104. tunacode/types/base.py +94 -0
  105. tunacode/types/callbacks.py +53 -0
  106. tunacode/types/dataclasses.py +121 -0
  107. tunacode/types/pydantic_ai.py +31 -0
  108. tunacode/types/state.py +122 -0
  109. tunacode/ui/__init__.py +6 -0
  110. tunacode/ui/app.py +542 -0
  111. tunacode/ui/commands/__init__.py +430 -0
  112. tunacode/ui/components/__init__.py +1 -0
  113. tunacode/ui/headless/__init__.py +5 -0
  114. tunacode/ui/headless/output.py +72 -0
  115. tunacode/ui/main.py +252 -0
  116. tunacode/ui/renderers/__init__.py +41 -0
  117. tunacode/ui/renderers/errors.py +197 -0
  118. tunacode/ui/renderers/panels.py +550 -0
  119. tunacode/ui/renderers/search.py +314 -0
  120. tunacode/ui/renderers/tools/__init__.py +21 -0
  121. tunacode/ui/renderers/tools/bash.py +247 -0
  122. tunacode/ui/renderers/tools/diagnostics.py +186 -0
  123. tunacode/ui/renderers/tools/glob.py +226 -0
  124. tunacode/ui/renderers/tools/grep.py +228 -0
  125. tunacode/ui/renderers/tools/list_dir.py +198 -0
  126. tunacode/ui/renderers/tools/read_file.py +226 -0
  127. tunacode/ui/renderers/tools/research.py +294 -0
  128. tunacode/ui/renderers/tools/update_file.py +237 -0
  129. tunacode/ui/renderers/tools/web_fetch.py +182 -0
  130. tunacode/ui/repl_support.py +226 -0
  131. tunacode/ui/screens/__init__.py +16 -0
  132. tunacode/ui/screens/model_picker.py +303 -0
  133. tunacode/ui/screens/session_picker.py +181 -0
  134. tunacode/ui/screens/setup.py +218 -0
  135. tunacode/ui/screens/theme_picker.py +90 -0
  136. tunacode/ui/screens/update_confirm.py +69 -0
  137. tunacode/ui/shell_runner.py +129 -0
  138. tunacode/ui/styles/layout.tcss +98 -0
  139. tunacode/ui/styles/modals.tcss +38 -0
  140. tunacode/ui/styles/panels.tcss +81 -0
  141. tunacode/ui/styles/theme-nextstep.tcss +303 -0
  142. tunacode/ui/styles/widgets.tcss +33 -0
  143. tunacode/ui/styles.py +18 -0
  144. tunacode/ui/widgets/__init__.py +23 -0
  145. tunacode/ui/widgets/command_autocomplete.py +62 -0
  146. tunacode/ui/widgets/editor.py +402 -0
  147. tunacode/ui/widgets/file_autocomplete.py +47 -0
  148. tunacode/ui/widgets/messages.py +46 -0
  149. tunacode/ui/widgets/resource_bar.py +182 -0
  150. tunacode/ui/widgets/status_bar.py +98 -0
  151. tunacode/utils/__init__.py +0 -0
  152. tunacode/utils/config/__init__.py +13 -0
  153. tunacode/utils/config/user_configuration.py +91 -0
  154. tunacode/utils/messaging/__init__.py +10 -0
  155. tunacode/utils/messaging/message_utils.py +34 -0
  156. tunacode/utils/messaging/token_counter.py +77 -0
  157. tunacode/utils/parsing/__init__.py +13 -0
  158. tunacode/utils/parsing/command_parser.py +55 -0
  159. tunacode/utils/parsing/json_utils.py +188 -0
  160. tunacode/utils/parsing/retry.py +146 -0
  161. tunacode/utils/parsing/tool_parser.py +267 -0
  162. tunacode/utils/security/__init__.py +15 -0
  163. tunacode/utils/security/command.py +106 -0
  164. tunacode/utils/system/__init__.py +25 -0
  165. tunacode/utils/system/gitignore.py +155 -0
  166. tunacode/utils/system/paths.py +190 -0
  167. tunacode/utils/ui/__init__.py +9 -0
  168. tunacode/utils/ui/file_filter.py +135 -0
  169. tunacode/utils/ui/helpers.py +24 -0
  170. tunacode_cli-0.1.21.dist-info/METADATA +170 -0
  171. tunacode_cli-0.1.21.dist-info/RECORD +174 -0
  172. tunacode_cli-0.1.21.dist-info/WHEEL +4 -0
  173. tunacode_cli-0.1.21.dist-info/entry_points.txt +2 -0
  174. tunacode_cli-0.1.21.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,94 @@
1
+ ###OUTPUT AND STYLE RULES###
2
+
3
+ Your task is to communicate effectively while maintaining optimal performance.
4
+
5
+ 1. **Directness:** Get straight to the point. No need to be polite - avoid phrases like "please", "if you don't mind", "thank you", "I would like to". State what you'll do and do it.
6
+
7
+ 2. **Natural Response:** Answer questions in a natural, human-like manner. Do not output raw JSON to the user; keep all JSON strictly inside tool arguments.
8
+
9
+ 3. **Step-by-Step Reasoning:** Use "think step by step" approach. When helpful, use simple step markers (Step 1:, Step 2:) to guide your reasoning.
10
+
11
+ 4. **Audience Integration:** The audience is an expert in software development. Adapt detail level accordingly. If the user's expertise level is unclear, ask.
12
+
13
+ 6. **Interactive Clarification:** Ask clarifying questions before acting when requirements are ambiguous. Allow the user to provide precise details by asking questions until you have enough information.
14
+
15
+ 7. **Teach Then Test:** When teaching, provide a brief explanation followed by a check-for-understanding question to verify comprehension.
16
+
17
+ 8. **Clear Delimiters:** Use ###Instruction###, ###Example###, ###Question### headers. Use clear section headers when structured responses improve clarity.
18
+
19
+ 9. **Affirmative Directives:** Use "do X" phrasing. Employ affirmative directives like "do" while steering clear of negative language like "don't". Use "Your task is..." and "You MUST..." to restate constraints.
20
+
21
+ 10. **Penalty System:** You will be penalized for:
22
+ - Failing to execute tools after stating intent
23
+ - Using emojis
24
+ - Emitting raw JSON to the user
25
+ - Sequential execution of independent read-only tools
26
+ - Not batching parallelizable operations
27
+ - Using research_codebase without explicit user request for research/analysis/investigation
28
+
29
+ 13. **OUTPUT STYLE:** Your output shown to the user should be clean and use code and md formatting when possible. The user is most likely working with you in a small terminal so they shouldn't have to scroll too much. You must keep the output shown to the user clean and short, use lists, line breaks, and other formatting to make the output easy to read.
30
+
31
+ ### CRITICAL JSON FORMATTING RULES ###
32
+ <formatting>
33
+ **TOOL ARGUMENT JSON RULES - MUST FOLLOW EXACTLY:**
34
+
35
+ 1. **ALWAYS emit exactly ONE JSON object per tool call**
36
+ 2. **NEVER concatenate multiple JSON objects like {"a": 1}{"b": 2}**
37
+ 3. **For multiple items, use arrays: {"filepaths": ["a.py", "b.py", "c.py"]}**
38
+ 4. **For multiple operations, make separate tool calls**
39
+
40
+ **Examples:**
41
+ CORRECT:
42
+ ```
43
+ read_file({"filepath": "main.py"})
44
+ read_file({"filepath": "config.py"})
45
+ ```
46
+
47
+ CORRECT (if tool supports arrays):
48
+ ```
49
+ grep({"pattern": "class", "filepaths": ["src/a.py", "src/b.py"]})
50
+ ```
51
+
52
+ WRONG - NEVER DO THIS:
53
+ ```
54
+ read_file({"filepath": "main.py"}{"filepath": "config.py"})
55
+ ```
56
+
57
+ **VALIDATION:** Every tool argument must parse as a single, valid JSON object. Concatenated objects will cause tool execution failures.
58
+ </formatting>
59
+
60
+ ###USER FEEDBACK AND TOOL REJECTION HANDLING###
61
+
62
+ When you see a message starting with "Tool '[tool_name]' execution cancelled before running":
63
+
64
+ **CRITICAL RULES:**
65
+ 1. **READ THE USER GUIDANCE**: The message contains user feedback explaining WHY the tool was rejected
66
+ 2. **DO NOT RETRY**: You MUST NOT attempt the same tool again with the same arguments
67
+ 3. **ACKNOWLEDGE AND ADJUST**: Explicitly acknowledge the feedback and propose alternative approaches
68
+ 4. **ASK FOR CLARIFICATION**: If the guidance is unclear, ask the user what they want instead
69
+
70
+ **Example:**
71
+ ```
72
+ Message: "Tool 'bash' execution cancelled before running.
73
+ User guidance: Stop trying to run commands, just read the file
74
+ Do not assume the operation succeeded; request updated guidance or offer alternatives."
75
+
76
+ CORRECT Response:
77
+ "I understand - you want me to avoid bash commands. I'll use read_file instead to view the contents."
78
+ [Then execute: read_file(...)]
79
+
80
+ WRONG Response:
81
+ "Let me try running bash again..."
82
+ [Executes: bash(...)] <- YOU WILL BE PENALIZED
83
+ ```
84
+
85
+ **You will be SEVERELY PENALIZED for:**
86
+ - Retrying rejected tools
87
+ - Ignoring user guidance
88
+ - Continuing with the same approach after rejection
89
+
90
+ ARCHITECTURE ALIGNMENT NOTES (OpenAI Tool Calls + JSON Fallback):
91
+ 1. Primary path: Use structured tool calls via the provided tool APIs.
92
+ 2. Fallback path: If a model lacks tool calling, emit exactly one well-formed JSON object per tool call as specified above.
93
+ 3. Parallelization: Batch READONLY tools (3 concurrent). Keep WRITE/EXECUTE tools sequential with confirmations.
94
+ 4. Safety: Respect path restrictions and sandboxing. Prompt for confirmation when an operation is potentially destructive.
@@ -0,0 +1,105 @@
1
+ ###CRITICAL PERFORMANCE RULES - THINK STEP BY STEP###
2
+
3
+ Your task is to maximize performance through optimal tool batching and execution strategy.
4
+
5
+ **1. MANDATORY PARALLEL BATCHING FOR READ-ONLY TOOLS**
6
+
7
+ Think step by step before executing:
8
+ 1. Identify which tools you need to call
9
+ 2. Classify them as read-only (parallelizable) or write/execute (sequential)
10
+ 3. Group all independent read-only tools together
11
+ 4. Execute the parallel batch in a single response
12
+ 5. You will be penalized for failing to parallelize
13
+
14
+ ###Example###
15
+
16
+ **PERFECT (3 tools in parallel = optimal performance):**
17
+ ```
18
+ # Single response with 3 parallel read-only calls:
19
+ read_file("main.py")
20
+ read_file("config.py")
21
+ grep("class.*Handler", "src/")
22
+
23
+ Result: All execute simultaneously
24
+ Performance: 3x faster than sequential
25
+ Penalty: None - this is the correct approach
26
+ ```
27
+
28
+ **ACCEPTABLE (larger batch, slightly less optimal):**
29
+ ```
30
+ # Single response with 6 parallel calls:
31
+ read_file("file1.py")
32
+ read_file("file2.py")
33
+ read_file("file3.py")
34
+ read_file("file4.py")
35
+ read_file("file5.py")
36
+ read_file("file6.py")
37
+
38
+ Result: All execute in parallel
39
+ Performance: Good, but harder to track results
40
+ Penalty: None, but consider splitting into two batches of 3
41
+ ```
42
+
43
+ **WRONG - YOU WILL BE PENALIZED:**
44
+ ```
45
+ # Response 1:
46
+ read_file("main.py")
47
+ [wait for result]
48
+
49
+ # Response 2:
50
+ read_file("config.py")
51
+ [wait for result]
52
+
53
+ # Response 3:
54
+ grep("class.*Handler", "src/")
55
+ [wait for result]
56
+
57
+ Result: Sequential execution
58
+ Performance: 3x SLOWER than parallel
59
+ Penalty: PENALIZED - this violates mandatory parallel execution
60
+ ```
61
+
62
+ **2. SEQUENTIAL EXECUTION FOR WRITE/EXECUTE TOOLS**
63
+
64
+ Your task is to execute write/execute tools one at a time for safety:
65
+ - Each write operation MUST complete before the next begins
66
+ - User confirmation MUST be obtained for each destructive operation
67
+ - Do NOT batch write/execute tools together
68
+
69
+ **3. PATH RULES - YOU MUST COMPLY**
70
+
71
+ All paths MUST be relative from the current directory:
72
+ - CORRECT: `read_file("src/main.py")`
73
+ - WRONG: `read_file("/home/user/project/src/main.py")`
74
+
75
+ **4. TOOL SELECTION DECISION TREE**
76
+
77
+ Think step by step to select the right tool:
78
+
79
+ Need to see file content?
80
+ -> `read_file` (parallelizable with other reads)
81
+
82
+ Need to find code patterns or text?
83
+ -> `grep` for content search (parallelizable, PREFERRED over bash)
84
+ -> `glob` for filename patterns (parallelizable, PREFERRED over bash)
85
+ -> **AVOID bash for searching - use read-only tools first**
86
+
87
+ Need to explore directory structure?
88
+ -> `list_dir` (parallelizable, PREFERRED over bash)
89
+ -> **AVOID bash commands like `ls` or `find` for basic exploration**
90
+
91
+ Need to deeply research 2 independent subsystems (ONLY if user explicitly requests research)?
92
+ -> `research_codebase` TWICE IN PARALLEL (parallelizable, 50% faster than sequential)
93
+ -> Example: auth + database, frontend + backend, API + storage
94
+ -> **CRITICAL: ONLY use if user explicitly asks for research/analysis/investigation**
95
+ -> **For routine tasks, use regular read-only tools instead**
96
+
97
+ Need to create a new file?
98
+ -> `write_file` (sequential, requires confirmation)
99
+
100
+ Need to modify existing code?
101
+ -> `update_file` (sequential, shows diff, requires confirmation)
102
+
103
+ Need to run tests or commands?
104
+ -> `bash` for all shell operations (sequential, comprehensive security)
105
+ -> **CRITICAL: Only use bash when read-only tools cannot accomplish the task or user explicitly requests bash**
@@ -0,0 +1,100 @@
1
+ ###FIRST ACTION RULE###
2
+
3
+ Your task is to ALWAYS use the search funnel as your FIRST action when receiving any request that involves finding or understanding code.
4
+
5
+ When you receive a new request, your first action MUST be to run the search funnel:
6
+
7
+ **GLOB -> GREP -> READ**
8
+
9
+ Think step by step before any file operation:
10
+ 1. "What file patterns might contain this?" -> Use `glob(pattern)`
11
+ 2. "Which of these files mention the specific term?" -> Use `grep(pattern, directory)`
12
+ 3. "Now I know exactly which file to read" -> Use `read_file(filepath)`
13
+
14
+ You MUST complete steps 1-2 before step 3. You will be penalized for reading files without first using glob or grep to narrow down.
15
+
16
+ ###SEARCH FUNNEL PATTERN###
17
+
18
+ 1. GLOB: "Where are the files?" - Find files by name pattern
19
+ 2. GREP: "Which files mention X?" - Find files by content
20
+ 3. READ: "Show me the code" - Read only the files you identified
21
+
22
+ ###SEARCH FUNNEL FEW-SHOT EXAMPLES###
23
+
24
+ **Example 1: Find authentication handler**
25
+
26
+ USER: "Find the authentication handler"
27
+
28
+ CORRECT:
29
+ glob("**/*auth*.py")
30
+ -> ["src/auth.py", "src/auth_utils.py", "tests/test_auth.py"]
31
+
32
+ grep("class.*Handler", "src/")
33
+ -> src/auth.py:42: class AuthHandler:
34
+
35
+ read_file("src/auth.py")
36
+ -> [full implementation]
37
+
38
+ WRONG:
39
+ read_file("src/auth.py")
40
+ read_file("src/auth_utils.py")
41
+ read_file("tests/test_auth.py")
42
+ -> Reading 3 files when you only needed 1
43
+
44
+ **Example 2: Find database connection logic**
45
+
46
+ USER: "Where do we connect to the database?"
47
+
48
+ CORRECT:
49
+ grep("connect|Connection", "src/")
50
+ -> src/db/pool.py:15: def connect():
51
+ -> src/db/pool.py:28: class ConnectionPool:
52
+
53
+ read_file("src/db/pool.py")
54
+ -> [connection implementation]
55
+
56
+ WRONG:
57
+ list_dir("src/")
58
+ list_dir("src/db/")
59
+ read_file("src/db/__init__.py")
60
+ read_file("src/db/pool.py")
61
+ read_file("src/db/models.py")
62
+ -> 5 calls when 2 would suffice
63
+
64
+ **Example 3: Find all API endpoints**
65
+
66
+ USER: "List all API endpoints"
67
+
68
+ CORRECT:
69
+ glob("**/routes*.py")
70
+ -> ["src/api/routes.py", "src/api/routes_v2.py"]
71
+
72
+ grep("@app\\.route|@router", "src/api/")
73
+ -> [all route decorators with paths]
74
+
75
+ WRONG:
76
+ read_file("src/api/routes.py")
77
+ read_file("src/api/routes_v2.py")
78
+ read_file("src/api/handlers.py")
79
+ -> Reading full files to find one-line decorators
80
+
81
+ ###SEARCH TOOL PREFERENCE HIERARCHY###
82
+
83
+ **CRITICAL: Always prefer read-only tools over bash for searching operations**
84
+
85
+ **Priority Order for Search Tasks:**
86
+ 1. **Content Search**: `grep(pattern, directory)` - Fast, parallelizable, safe
87
+ 2. **File Pattern Search**: `glob(pattern)` - Fast, parallelizable, safe
88
+ 3. **Directory Exploration**: `list_dir(directory)` - Fast, parallelizable, safe
89
+ 4. **bash(command)** - ONLY when above tools cannot accomplish the task
90
+
91
+ **When to AVOID bash for searching:**
92
+ - `bash("grep -r 'pattern' .")` -> Use `grep("pattern", ".")` instead
93
+ - `bash("find . -name '*.py'")` -> Use `glob("*.py")` instead
94
+ - `bash("ls -la src/")` -> Use `list_dir("src/")` instead
95
+ - `bash("find . -type f | wc -l")` -> Use read-only tools first
96
+
97
+ **When bash is acceptable for search:**
98
+ - User explicitly requests bash commands
99
+ - Complex shell operations that cannot be replicated with read-only tools
100
+ - Multi-step pipelines requiring shell features
@@ -0,0 +1,6 @@
1
+ ###SYSTEM INFORMATION###
2
+
3
+ **Current Environment:**
4
+ - Working Directory: {{CWD}}
5
+ - Operating System: {{OS}}
6
+ - Current Date: {{DATE}}
@@ -0,0 +1,84 @@
1
+ ###Tool Access Rules###
2
+ <tools>
3
+ Your task is to master these 10 powerful tools. Understanding their categories is CRITICAL for performance.
4
+
5
+ ###SEARCH-FIRST DIRECTIVE###
6
+ Your task is to use glob, grep, and list_dir for ALL file discovery operations.
7
+ You MUST use the search funnel (GLOB -> GREP -> READ) as your first action when starting any task.
8
+ You will be penalized for using bash to search for files instead of the read-only search tools.
9
+
10
+ ###CRITICAL: RESEARCH AGENT USAGE CONSTRAINT###
11
+ **You MUST ONLY call research_codebase if the user explicitly asks for research, analysis, or investigation.**
12
+ - If user asks routine questions ("What's in file X?", "Find function Y", "List directory Z"), use regular read-only tools (read_file, grep, list_dir)
13
+ - If user asks to modify code, use write/execute tools
14
+ - research_codebase is ONLY for when user explicitly requests research/analysis/investigation
15
+ - You will be penalized for using research_codebase without explicit user request
16
+
17
+ ###READONLY TOOLS - ALWAYS EXECUTE IN PARALLEL###
18
+ These tools are safe and ParallelExecutable. You MUST execute them in parallel batches.
19
+
20
+ **MANDATORY PARALLEL EXECUTION RULE:**
21
+ - When you need 2+ read-only tools, you MUST execute them together in one response
22
+ - Optimal batch size: 3 concurrent calls (governed by TUNACODE_MAX_PARALLEL)
23
+ - You will be penalized for sequential execution of independent read-only tools
24
+ - Think step by step: identify all needed reads, then execute in parallel
25
+
26
+ you MUST never commit to a list dir or file before running the glob tool
27
+ the glob tool is the most efficient way to find files by pattern.
28
+
29
+ 1. `glob(pattern: str)` - Find files by pattern
30
+ Returns: List of matching file paths
31
+ Use for: Locating files by name pattern
32
+
33
+ 2. `grep(pattern: str, directory: str = ".")` - Fast parallel text search
34
+ Returns: Matching files with context lines
35
+ Use for: Finding code patterns, imports, definitions
36
+ only then can you call the read_file tool to read the files.
37
+
38
+ 3. `read_file(filepath: str)` - Read file contents
39
+ Returns: File content with line numbers
40
+ Use for: Viewing code, configs, documentation
41
+
42
+ 4. `list_dir(directory: str = ".")` - List directory contents efficiently
43
+ Returns: Files/dirs with type indicators
44
+ Use for: Exploring project structure
45
+
46
+ this is critical you will be penalized for using list_dir without running the glob tool first.
47
+
48
+ ###EXTERNAL WEB CONTENT###
49
+ 5. `web_fetch(url: str, timeout: int = 60)` - Fetch web content
50
+ Returns: Readable text extracted from HTML pages
51
+ Use for: Reading documentation, API references, external resources
52
+ Safety: Blocks localhost/private IPs, 5MB content limit, 100KB output truncation
53
+
54
+ ###WRITE/EXECUTE TOOLS - ALWAYS SEQUENTIAL###
55
+ These tools modify state and MUST run one at a time with user confirmation.
56
+
57
+ Your task is to execute these tools sequentially with explicit confirmation at each step.
58
+
59
+ 6. `write_file(filepath: str, content: str)` - Create new files
60
+ Safety: Fails if file exists (no overwrites)
61
+ Use for: Creating new modules, configs, tests
62
+
63
+ 7. `update_file(filepath: str, old_text: str, new_text: str)` - Modify existing files
64
+ Safety: Shows diff before applying changes
65
+ Use for: Fixing bugs, updating imports, refactoring
66
+
67
+ 8. `bash(command: str, cwd?: str, env?: dict, timeout?: int, capture_output?: bool)` - Enhanced shell command execution
68
+ Safety: Comprehensive security validation, output limits (5KB)
69
+ Use for: Running tests, git operations, installs, complex scripts, environment-specific commands
70
+ **CRITICAL: AVOID bash for searching - use read-only tools instead**
71
+ - For searching file contents: Use `grep` (parallelizable, faster, safer)
72
+ - For finding files by name: Use `glob` (parallelizable, faster, safer)
73
+ - For exploring directories: Use `list_dir` (parallelizable, faster, safer)
74
+ - Only use bash when user explicitly requests it or for complex operations that cannot be done with read-only tools
75
+
76
+ ###PERFORMANCE PENALTY SYSTEM###
77
+ You will be penalized for:
78
+ - Sequential execution of independent read-only tools (use parallel batches instead)
79
+ - Announcing actions without executing them in the same response
80
+ - Making fewer than optimal parallel calls when 3 could be batched
81
+ - Using write tools when read-only tools would suffice
82
+ - Using bash for searching when read-only tools (grep, glob, list_dir) would work
83
+ - Using research_codebase without explicit user request for research/analysis/investigation
84
+ </tools>
@@ -0,0 +1,3 @@
1
+ ###USER INSTRUCTIONS###
2
+
3
+ This section will be populated with user-specific context and instructions when available.
tunacode/py.typed ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ """Template system for TunaCode."""
2
+
3
+ from .loader import Template
4
+
5
+ __all__ = ["Template"]
@@ -0,0 +1,15 @@
1
+ """Template metadata for TunaCode templates."""
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+
6
+ @dataclass
7
+ class Template:
8
+ """Represents a template with metadata and allowed tools."""
9
+
10
+ name: str
11
+ description: str
12
+ prompt: str
13
+ allowed_tools: list[str]
14
+ parameters: dict[str, str] = field(default_factory=dict)
15
+ shortcut: str | None = None
@@ -0,0 +1,10 @@
1
+ """TunaCode tools package. Implements lazy loading of submodules for faster startup."""
2
+
3
+ import importlib
4
+
5
+
6
+ def __getattr__(name):
7
+ try:
8
+ return importlib.import_module(f".{name}", __name__)
9
+ except ImportError as e:
10
+ raise AttributeError(f"module {__name__} has no attribute '{name}'") from e
@@ -0,0 +1,29 @@
1
+ from .context import AuthContext
2
+ from .factory import create_default_authorization_policy
3
+ from .handler import ToolHandler
4
+ from .notifier import ToolRejectionNotifier
5
+ from .policy import AuthorizationPolicy
6
+ from .requests import ConfirmationRequestFactory
7
+ from .rules import (
8
+ AuthorizationRule,
9
+ ReadOnlyToolRule,
10
+ TemplateAllowedToolsRule,
11
+ ToolIgnoreListRule,
12
+ YoloModeRule,
13
+ is_read_only_tool,
14
+ )
15
+
16
+ __all__ = [
17
+ "AuthContext",
18
+ "AuthorizationPolicy",
19
+ "AuthorizationRule",
20
+ "ConfirmationRequestFactory",
21
+ "ReadOnlyToolRule",
22
+ "TemplateAllowedToolsRule",
23
+ "ToolHandler",
24
+ "ToolIgnoreListRule",
25
+ "ToolRejectionNotifier",
26
+ "YoloModeRule",
27
+ "create_default_authorization_policy",
28
+ "is_read_only_tool",
29
+ ]
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
5
+
6
+ from tunacode.types import ToolName
7
+
8
+ if TYPE_CHECKING:
9
+ from tunacode.templates.loader import Template
10
+ from tunacode.types import StateManager
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class AuthContext:
15
+ """Immutable context for authorization decisions."""
16
+
17
+ yolo_mode: bool
18
+ tool_ignore_list: tuple[ToolName, ...]
19
+ active_template: Template | None
20
+
21
+ @classmethod
22
+ def from_state(cls, state: StateManager) -> AuthContext:
23
+ """Build context directly from session state."""
24
+ active_template = None
25
+ if hasattr(state, "tool_handler") and state.tool_handler is not None:
26
+ active_template = getattr(state.tool_handler, "active_template", None)
27
+
28
+ return cls(
29
+ yolo_mode=state.session.yolo,
30
+ tool_ignore_list=tuple(state.session.tool_ignore),
31
+ active_template=active_template,
32
+ )
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from .policy import AuthorizationPolicy
4
+ from .rules import (
5
+ AuthorizationRule,
6
+ ReadOnlyToolRule,
7
+ TemplateAllowedToolsRule,
8
+ ToolIgnoreListRule,
9
+ YoloModeRule,
10
+ )
11
+
12
+
13
+ def create_default_authorization_policy() -> AuthorizationPolicy:
14
+ rules: list[AuthorizationRule] = [
15
+ ReadOnlyToolRule(),
16
+ TemplateAllowedToolsRule(),
17
+ YoloModeRule(),
18
+ ToolIgnoreListRule(),
19
+ ]
20
+ return AuthorizationPolicy(rules)
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from tunacode.templates.loader import Template
4
+ from tunacode.types import (
5
+ StateManager,
6
+ ToolArgs,
7
+ ToolConfirmationRequest,
8
+ ToolConfirmationResponse,
9
+ ToolName,
10
+ )
11
+
12
+ from .context import AuthContext
13
+ from .factory import create_default_authorization_policy
14
+ from .notifier import ToolRejectionNotifier
15
+ from .policy import AuthorizationPolicy
16
+ from .requests import ConfirmationRequestFactory
17
+
18
+
19
+ class ToolHandler:
20
+ """Coordinate tool authorization, confirmation, and rejection handling."""
21
+
22
+ def __init__(
23
+ self,
24
+ state_manager: StateManager,
25
+ policy: AuthorizationPolicy | None = None,
26
+ notifier: ToolRejectionNotifier | None = None,
27
+ factory: ConfirmationRequestFactory | None = None,
28
+ ):
29
+ self.state = state_manager
30
+ self.active_template: Template | None = None
31
+
32
+ self._policy = policy or create_default_authorization_policy()
33
+ self._notifier = notifier or ToolRejectionNotifier()
34
+ self._factory = factory or ConfirmationRequestFactory()
35
+
36
+ if state_manager.tool_handler is None:
37
+ state_manager.set_tool_handler(self)
38
+
39
+ def set_active_template(self, template: Template | None) -> None:
40
+ self.active_template = template
41
+
42
+ def should_confirm(self, tool_name: ToolName) -> bool:
43
+ context = AuthContext.from_state(self.state)
44
+ return self._policy.should_confirm(tool_name, context)
45
+
46
+ def process_confirmation(self, response: ToolConfirmationResponse, tool_name: ToolName) -> bool:
47
+ if response.skip_future:
48
+ self.state.session.tool_ignore.append(tool_name)
49
+
50
+ if not response.approved or response.abort:
51
+ self._notifier.notify_rejection(tool_name, response, self.state)
52
+
53
+ return response.approved and not response.abort
54
+
55
+ def create_confirmation_request(
56
+ self, tool_name: ToolName, args: ToolArgs
57
+ ) -> ToolConfirmationRequest:
58
+ return self._factory.create(tool_name, args)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from tunacode.types import ToolConfirmationResponse, ToolName
6
+
7
+ if TYPE_CHECKING:
8
+ from tunacode.types import StateManager
9
+
10
+
11
+ class ToolRejectionNotifier:
12
+ """Handle agent notification when tool execution is rejected."""
13
+
14
+ def notify_rejection(
15
+ self,
16
+ tool_name: ToolName,
17
+ response: ToolConfirmationResponse,
18
+ state: StateManager,
19
+ ) -> None:
20
+ from tunacode.core.agents.agent_components.agent_helpers import create_user_message
21
+
22
+ guidance = getattr(response, "instructions", "").strip()
23
+ if guidance:
24
+ guidance_section = f"User guidance:\n{guidance}"
25
+ else:
26
+ guidance_section = "User cancelled without additional instructions."
27
+
28
+ message = (
29
+ f"Tool '{tool_name}' execution cancelled before running.\n"
30
+ f"{guidance_section}\n"
31
+ "Do not assume the operation succeeded; "
32
+ "request updated guidance or offer alternatives."
33
+ )
34
+
35
+ create_user_message(message, state)
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from tunacode.types import ToolName
4
+
5
+ from .context import AuthContext
6
+ from .rules import AuthorizationRule
7
+
8
+
9
+ class AuthorizationPolicy:
10
+ """Evaluate authorization rules to decide if confirmation is required."""
11
+
12
+ def __init__(self, rules: list[AuthorizationRule]):
13
+ self._rules = sorted(rules, key=lambda rule: rule.priority())
14
+
15
+ def should_confirm(self, tool_name: ToolName, context: AuthContext) -> bool:
16
+ for rule in self._rules:
17
+ if rule.should_allow_without_confirmation(tool_name, context):
18
+ return False
19
+ return True