jarvis-ai-assistant 0.1.134__py3-none-any.whl → 0.1.138__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 jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (78) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +201 -79
  3. jarvis/jarvis_agent/builtin_input_handler.py +16 -6
  4. jarvis/jarvis_agent/file_input_handler.py +9 -9
  5. jarvis/jarvis_agent/jarvis.py +10 -10
  6. jarvis/jarvis_agent/main.py +12 -11
  7. jarvis/jarvis_agent/output_handler.py +3 -3
  8. jarvis/jarvis_agent/patch.py +86 -62
  9. jarvis/jarvis_agent/shell_input_handler.py +5 -3
  10. jarvis/jarvis_code_agent/code_agent.py +134 -99
  11. jarvis/jarvis_code_agent/file_select.py +24 -24
  12. jarvis/jarvis_dev/main.py +45 -51
  13. jarvis/jarvis_git_details/__init__.py +0 -0
  14. jarvis/jarvis_git_details/main.py +179 -0
  15. jarvis/jarvis_git_squash/main.py +7 -7
  16. jarvis/jarvis_lsp/base.py +11 -11
  17. jarvis/jarvis_lsp/cpp.py +14 -14
  18. jarvis/jarvis_lsp/go.py +13 -13
  19. jarvis/jarvis_lsp/python.py +8 -8
  20. jarvis/jarvis_lsp/registry.py +21 -21
  21. jarvis/jarvis_lsp/rust.py +15 -15
  22. jarvis/jarvis_methodology/main.py +101 -0
  23. jarvis/jarvis_multi_agent/__init__.py +11 -11
  24. jarvis/jarvis_multi_agent/main.py +6 -6
  25. jarvis/jarvis_platform/__init__.py +1 -1
  26. jarvis/jarvis_platform/ai8.py +67 -89
  27. jarvis/jarvis_platform/base.py +14 -13
  28. jarvis/jarvis_platform/kimi.py +25 -28
  29. jarvis/jarvis_platform/ollama.py +24 -26
  30. jarvis/jarvis_platform/openai.py +15 -19
  31. jarvis/jarvis_platform/oyi.py +48 -50
  32. jarvis/jarvis_platform/registry.py +27 -28
  33. jarvis/jarvis_platform/yuanbao.py +38 -42
  34. jarvis/jarvis_platform_manager/main.py +81 -81
  35. jarvis/jarvis_platform_manager/openai_test.py +21 -21
  36. jarvis/jarvis_rag/file_processors.py +18 -18
  37. jarvis/jarvis_rag/main.py +261 -277
  38. jarvis/jarvis_smart_shell/main.py +12 -12
  39. jarvis/jarvis_tools/ask_codebase.py +28 -28
  40. jarvis/jarvis_tools/ask_user.py +8 -8
  41. jarvis/jarvis_tools/base.py +4 -4
  42. jarvis/jarvis_tools/chdir.py +9 -9
  43. jarvis/jarvis_tools/code_review.py +19 -19
  44. jarvis/jarvis_tools/create_code_agent.py +15 -15
  45. jarvis/jarvis_tools/execute_python_script.py +3 -3
  46. jarvis/jarvis_tools/execute_shell.py +11 -11
  47. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  48. jarvis/jarvis_tools/file_analyzer.py +29 -29
  49. jarvis/jarvis_tools/file_operation.py +22 -20
  50. jarvis/jarvis_tools/find_caller.py +25 -25
  51. jarvis/jarvis_tools/find_methodolopy.py +65 -0
  52. jarvis/jarvis_tools/find_symbol.py +24 -24
  53. jarvis/jarvis_tools/function_analyzer.py +27 -27
  54. jarvis/jarvis_tools/git_commiter.py +9 -9
  55. jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
  56. jarvis/jarvis_tools/methodology.py +23 -62
  57. jarvis/jarvis_tools/project_analyzer.py +29 -33
  58. jarvis/jarvis_tools/rag.py +15 -15
  59. jarvis/jarvis_tools/read_code.py +24 -22
  60. jarvis/jarvis_tools/read_webpage.py +31 -31
  61. jarvis/jarvis_tools/registry.py +72 -52
  62. jarvis/jarvis_tools/tool_generator.py +18 -18
  63. jarvis/jarvis_utils/config.py +23 -23
  64. jarvis/jarvis_utils/embedding.py +83 -83
  65. jarvis/jarvis_utils/git_utils.py +20 -20
  66. jarvis/jarvis_utils/globals.py +18 -6
  67. jarvis/jarvis_utils/input.py +10 -9
  68. jarvis/jarvis_utils/methodology.py +140 -136
  69. jarvis/jarvis_utils/output.py +11 -11
  70. jarvis/jarvis_utils/utils.py +22 -70
  71. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +1 -1
  72. jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
  73. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +2 -0
  74. jarvis/jarvis_tools/select_code_files.py +0 -62
  75. jarvis_ai_assistant-0.1.134.dist-info/RECORD +0 -82
  76. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
  77. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
  78. {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,12 @@
1
- import subprocess
1
+ """Jarvis代码代理模块。
2
+
3
+ 该模块提供CodeAgent类,用于处理代码修改任务。
4
+ """
5
+
2
6
  import os
7
+ import sys
8
+ import subprocess
3
9
  import argparse
4
- from token import OP
5
10
  from typing import Optional
6
11
 
7
12
  from yaspin import yaspin
@@ -13,41 +18,51 @@ from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
13
18
  from jarvis.jarvis_agent.patch import PatchOutputHandler
14
19
  from jarvis.jarvis_platform.registry import PlatformRegistry
15
20
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
16
-
17
21
  from jarvis.jarvis_tools.registry import ToolRegistry
18
- from jarvis.jarvis_utils.git_utils import find_git_root, get_commits_between, get_latest_commit_hash, has_uncommitted_changes
22
+ from jarvis.jarvis_utils.git_utils import (
23
+ find_git_root,
24
+ get_commits_between,
25
+ get_latest_commit_hash,
26
+ has_uncommitted_changes
27
+ )
19
28
  from jarvis.jarvis_utils.input import get_multiline_input
20
29
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
30
  from jarvis.jarvis_utils.utils import init_env, user_confirm
22
31
 
23
32
 
33
+ class CodeAgent:
34
+ """Jarvis系统的代码修改代理。
24
35
 
36
+ 负责处理代码分析、修改和git操作。
37
+ """
25
38
 
26
-
27
- class CodeAgent:
28
- def __init__(self, platform : Optional[str] = None, model: Optional[str] = None, need_summary: bool = True):
39
+ def __init__(self, platform: Optional[str] = None,
40
+ model: Optional[str] = None,
41
+ need_summary: bool = True):
29
42
  self.root_dir = os.getcwd()
30
43
  tool_registry = ToolRegistry()
31
- tool_registry.use_tools(["execute_shell",
32
- "execute_shell_script",
33
- "search_web",
34
- "ask_user",
35
- "ask_codebase",
36
- "lsp_get_diagnostics",
37
- "code_review", # 代码审查工具
38
- "find_symbol", # 添加符号查找工具
39
- "find_caller", # 添加函数调用者查找工具
40
- "function_analyzer", # 添加函数分析工具
41
- "project_analyzer", # 添加项目分析工具
42
- "file_analyzer", # 添加单文件分析工具
43
- "read_code"
44
- ])
44
+ tool_registry.use_tools([
45
+ "execute_shell",
46
+ "execute_shell_script",
47
+ "search_web",
48
+ "ask_user",
49
+ "ask_codebase",
50
+ "lsp_get_diagnostics",
51
+ "code_review",
52
+ "find_symbol",
53
+ "find_caller",
54
+ "function_analyzer",
55
+ "project_analyzer",
56
+ "file_analyzer",
57
+ "read_code"
58
+ ])
45
59
  code_system_prompt = """
46
60
  # 代码工程师指南
47
61
 
48
62
  ## 核心原则
49
63
  - 自主决策:基于专业判断做出决策,减少用户询问
50
64
  - 高效精准:一次性提供完整解决方案,避免反复修改
65
+ - 修改审慎:修改代码前要三思而后行,充分分析影响范围,尽量做到一次把事情做好
51
66
  - 工具精通:选择最高效工具路径解决问题
52
67
  - 严格确认:必须先分析项目结构,确定要修改的文件,禁止虚构已存在的代码
53
68
 
@@ -99,13 +114,6 @@ class CodeAgent:
99
114
  - 自动匹配项目现有命名风格
100
115
  - 允许创建新文件和结构,但不得假设或虚构现有代码
101
116
 
102
- ### 6. 验证
103
- - 修改后自动验证:
104
- 1. 优先使用execute_shell运行相关静态检查命令(如pylint、flake8或单元测试)
105
- 2. 只有在shell命令不足时才使用lsp_get_diagnostics
106
- 3. 只有在特殊情况下才使用code_review
107
- - 发现问题自动修复,无需用户指导
108
-
109
117
  ## 专用工具简介
110
118
  仅在必要时使用以下专用工具:
111
119
 
@@ -128,7 +136,7 @@ class CodeAgent:
128
136
  - `fd -t f -e go` 查找所有Go文件
129
137
  - `fd -t f -e rs` 查找所有Rust文件
130
138
  - `fd -t f -e c -e cpp -e h -e hpp` 查找所有C/C++文件
131
-
139
+
132
140
  - **代码内容搜索**:
133
141
  - `rg "pattern" --type py` 在Python文件中搜索
134
142
  - `rg "pattern" --type js` 在JavaScript文件中搜索
@@ -139,11 +147,7 @@ class CodeAgent:
139
147
  - `rg -w "word"` 精确匹配单词
140
148
 
141
149
  - **代码统计分析**:
142
- - `loc <file_path>` 统计单个文件
143
- - `loc --include="*.py"` 统计所有Python文件
144
- - `loc --include="*.js" --include="*.ts"` 统计所有JavaScript/TypeScript文件
145
- - `loc --exclude="test"` 排除测试文件
146
- - `loc --sort=code` 按代码量排序
150
+ - `loc` 统计当前目录代码行数
147
151
 
148
152
  - **代码质量检查**:
149
153
  - Python: `pylint <file_path>`, `flake8 <file_path>`
@@ -174,27 +178,37 @@ class CodeAgent:
174
178
  - fd比find更快更易用,应优先使用
175
179
  - loc比wc -l提供更多代码统计信息,应优先使用
176
180
  - 针对不同编程语言选择对应的代码质量检查工具
181
+ - 不要留下未实现的代码
177
182
  """
178
183
  # Dynamically add ask_codebase based on task complexity if really needed
179
184
  # 处理platform参数
180
- platform_instance = (PlatformRegistry().create_platform(platform)
181
- if platform
182
- else PlatformRegistry().get_normal_platform())
185
+ platform_instance = (PlatformRegistry().create_platform(platform)
186
+ if platform
187
+ else PlatformRegistry().get_normal_platform())
183
188
  if model:
184
- platform_instance.set_model_name(model) # type: ignore
185
-
189
+ platform_instance.set_model_name(model) # type: ignore
190
+
186
191
  self.agent = Agent(system_prompt=code_system_prompt,
187
192
  name="CodeAgent",
188
- auto_complete=False,
189
- output_handler=[tool_registry, PatchOutputHandler()],
193
+ auto_complete=False,
194
+ output_handler=[tool_registry,
195
+ PatchOutputHandler()],
190
196
  platform=platform_instance,
191
- input_handler=[shell_input_handler, file_input_handler, builtin_input_handler],
197
+ input_handler=[
198
+ shell_input_handler, file_input_handler, builtin_input_handler],
192
199
  need_summary=need_summary)
200
+ self.agent.set_addon_prompt("请使用工具充分理解用户需求,然后根据需求一步步执行代码修改/开发")
201
+
202
+ def get_root_dir(self) -> str:
203
+ """获取项目根目录
193
204
 
194
-
205
+ 返回:
206
+ str: 项目根目录路径
207
+ """
208
+ return self.root_dir
195
209
 
196
210
  def _init_env(self):
197
- with yaspin(text="正在初始化环境...", color="cyan") as spinner:
211
+ with yaspin(text="正在初始化环境...", color="cyan") as spinner:
198
212
  curr_dir = os.getcwd()
199
213
  git_dir = find_git_root(curr_dir)
200
214
  self.root_dir = git_dir
@@ -205,62 +219,86 @@ class CodeAgent:
205
219
  spinner.text = "环境初始化完成"
206
220
  spinner.ok("✅")
207
221
 
208
-
209
-
210
- def run(self, user_input: str) :
211
- """Run the code agent with the given user input.
212
-
213
- Args:
214
- user_input: The user's requirement/request
215
-
216
- Returns:
217
- str: Output describing the execution result
222
+ def _handle_uncommitted_changes(self):
223
+ """处理未提交的修改"""
224
+ if has_uncommitted_changes():
225
+ PrettyOutput.print("检测到未提交的修改,是否要提交?", OutputType.WARNING)
226
+ if user_confirm("是否要提交?", True):
227
+ git_commiter = GitCommitTool()
228
+ git_commiter.execute({})
229
+
230
+ def _show_commit_history(self, start_commit, end_commit):
231
+ """显示提交历史"""
232
+ if start_commit and end_commit:
233
+ commits = get_commits_between(start_commit, end_commit)
234
+ else:
235
+ commits = []
236
+
237
+ if commits:
238
+ commit_messages = "检测到以下提交记录:\n" + \
239
+ "\n".join(
240
+ [f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits])
241
+ PrettyOutput.print(commit_messages, OutputType.INFO)
242
+ return commits
243
+
244
+ def _handle_commit_confirmation(self, commits, start_commit):
245
+ """处理提交确认和可能的重置"""
246
+ if commits and user_confirm("是否接受以上提交记录?", True):
247
+ if len(commits) > 1 and user_confirm(
248
+ "是否要合并为一个更清晰的提交记录?", True
249
+ ):
250
+ # Reset to start commit
251
+ subprocess.run(
252
+ ["git", "reset", "--mixed", start_commit],
253
+ stdout=subprocess.DEVNULL,
254
+ stderr=subprocess.DEVNULL,
255
+ check=True
256
+ )
257
+ # Create new commit
258
+ git_commiter = GitCommitTool()
259
+ git_commiter.execute({})
260
+ elif start_commit:
261
+ os.system(f"git reset --hard {start_commit}")
262
+ PrettyOutput.print("已重置到初始提交", OutputType.INFO)
263
+
264
+ def run(self, user_input: str) -> Optional[str]:
265
+ """使用给定的用户输入运行代码代理。
266
+
267
+ 参数:
268
+ user_input: 用户的需求/请求
269
+
270
+ 返回:
271
+ str: 描述执行结果的输出,成功时返回None
218
272
  """
219
273
  try:
220
274
  self._init_env()
221
-
222
275
  start_commit = get_latest_commit_hash()
223
-
276
+
224
277
  try:
225
278
  self.agent.run(user_input)
226
- except Exception as e:
279
+ except RuntimeError as e:
227
280
  PrettyOutput.print(f"执行失败: {str(e)}", OutputType.WARNING)
228
-
281
+ return str(e)
282
+
283
+ self._handle_uncommitted_changes()
229
284
  end_commit = get_latest_commit_hash()
230
- # Print commit history between start and end commits
231
- if start_commit and end_commit:
232
- commits = get_commits_between(start_commit, end_commit)
233
- else:
234
- commits = []
235
-
236
- if commits:
237
- commit_messages = "检测到以下提交记录:\n" + "\n".join([f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits])
238
- PrettyOutput.print(commit_messages, OutputType.INFO)
239
-
240
- if commits and user_confirm("是否接受以上提交记录?", True):
241
- if len(commits) > 1 and user_confirm("是否要合并为一个更清晰的提交记录?", True):
242
- # Reset to start commit
243
- subprocess.run(["git", "reset", "--mixed", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
244
- # Create new commit
245
- git_commiter = GitCommitTool()
246
- git_commiter.execute({})
247
- elif start_commit:
248
- os.system(f"git reset --hard {start_commit}")
249
- PrettyOutput.print("已重置到初始提交", OutputType.INFO)
250
-
251
- except Exception as e:
285
+ commits = self._show_commit_history(start_commit, end_commit)
286
+ self._handle_commit_confirmation(commits, start_commit)
287
+ return None
288
+
289
+ except RuntimeError as e:
252
290
  return f"Error during execution: {str(e)}"
253
-
254
291
 
255
292
 
256
293
  def main():
257
- """Jarvis main entry point"""
258
- # Add argument parser
294
+ """Jarvis主入口点。"""
259
295
  init_env()
260
296
 
261
297
  parser = argparse.ArgumentParser(description='Jarvis Code Agent')
262
- parser.add_argument('-p', '--platform', type=str, help='Target platform name', default=None)
263
- parser.add_argument('-m', '--model', type=str, help='Model name to use', default=None)
298
+ parser.add_argument('-p', '--platform', type=str,
299
+ help='Target platform name', default=None)
300
+ parser.add_argument('-m', '--model', type=str,
301
+ help='Model name to use', default=None)
264
302
  args = parser.parse_args()
265
303
 
266
304
  curr_dir = os.getcwd()
@@ -268,21 +306,18 @@ def main():
268
306
  PrettyOutput.print(f"当前目录: {git_dir}", OutputType.INFO)
269
307
 
270
308
  try:
271
- try:
272
- user_input = get_multiline_input("请输入你的需求(输入空行退出):")
273
- if not user_input:
274
- return 0
275
- agent = CodeAgent(platform=args.platform, model=args.model, need_summary=False)
276
- agent.run(user_input)
277
-
278
- except Exception as e:
279
- PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
309
+ user_input = get_multiline_input("请输入你的需求(输入空行退出):")
310
+ if not user_input:
311
+ sys.exit(0)
312
+ agent = CodeAgent(platform=args.platform,
313
+ model=args.model,
314
+ need_summary=False)
315
+ agent.run(user_input)
280
316
 
281
- except Exception as e:
282
- PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
283
- return 1
317
+ except RuntimeError as e:
318
+ PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
319
+ sys.exit(1)
284
320
 
285
- return 0
286
321
 
287
322
  if __name__ == "__main__":
288
- exit(main())
323
+ main()
@@ -11,15 +11,15 @@ from jarvis.jarvis_utils.utils import user_confirm
11
11
 
12
12
  def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
13
13
  selected = set()
14
-
14
+
15
15
  # Remove all whitespace characters
16
16
  input_str = "".join(input_str.split())
17
-
17
+
18
18
  # Process comma-separated parts
19
19
  for part in input_str.split(","):
20
20
  if not part:
21
21
  continue
22
-
22
+
23
23
  # Process range (e.g.: 3-6)
24
24
  if "-" in part:
25
25
  try:
@@ -41,7 +41,7 @@ def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
41
41
  PrettyOutput.print(f"忽略超出范围的索引: {part}", OutputType.WARNING)
42
42
  except ValueError:
43
43
  PrettyOutput.print(f"忽略无效的数字: {part}", OutputType.WARNING)
44
-
44
+
45
45
  return sorted(list(selected))
46
46
 
47
47
  def _get_file_completer(root_dir: str) -> Completer:
@@ -49,58 +49,58 @@ def _get_file_completer(root_dir: str) -> Completer:
49
49
  class FileCompleter(Completer):
50
50
  def __init__(self, root_dir: str):
51
51
  self.root_dir = root_dir
52
-
52
+
53
53
  def get_completions(self, document, complete_event):
54
54
  text = document.text_before_cursor
55
-
55
+
56
56
  if not text:
57
57
  for path in self._list_files(""):
58
58
  yield Completion(path, start_position=0)
59
59
  return
60
-
60
+
61
61
  # Generate fuzzy matching pattern
62
62
  pattern = '.*'.join(map(re.escape, text))
63
63
  try:
64
64
  regex = re.compile(pattern, re.IGNORECASE)
65
65
  except re.error:
66
66
  return
67
-
67
+
68
68
  for path in self._list_files(""):
69
69
  if regex.search(path):
70
70
  yield Completion(path, start_position=-len(text))
71
-
71
+
72
72
  def _list_files(self, current_dir: str) -> List[str]:
73
73
  """List all files in the specified directory (recursively)"""
74
74
  files = []
75
75
  search_dir = os.path.join(self.root_dir, current_dir)
76
-
76
+
77
77
  for root, _, filenames in os.walk(search_dir):
78
78
  for filename in filenames:
79
79
  full_path = os.path.join(root, filename)
80
80
  rel_path = os.path.relpath(full_path, self.root_dir)
81
81
  if not any(part.startswith('.') for part in rel_path.split(os.sep)):
82
82
  files.append(rel_path)
83
-
83
+
84
84
  return sorted(files)
85
85
 
86
86
  return FileCompleter(root_dir)
87
87
 
88
88
  def _fuzzy_match_files(root_dir: str, pattern: str) -> List[str]:
89
89
  """Fuzzy match file path
90
-
90
+
91
91
  Args:
92
92
  pattern: Matching pattern
93
-
93
+
94
94
  Returns:
95
95
  List[str]: List of matching file paths
96
96
  """
97
97
  matches = []
98
-
98
+
99
99
  # 将模式转换为正则表达式
100
100
  pattern = pattern.replace('.', r'\.').replace('*', '.*').replace('?', '.')
101
101
  pattern = f".*{pattern}.*" # 允许部分匹配
102
102
  regex = re.compile(pattern, re.IGNORECASE)
103
-
103
+
104
104
  # 遍历所有文件
105
105
  for root, _, files in os.walk(root_dir):
106
106
  for file in files:
@@ -110,7 +110,7 @@ def _fuzzy_match_files(root_dir: str, pattern: str) -> List[str]:
110
110
  if not any(part.startswith('.') for part in rel_path.split(os.sep)):
111
111
  if regex.match(rel_path):
112
112
  matches.append(rel_path)
113
-
113
+
114
114
  return sorted(matches)
115
115
 
116
116
  def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dict[str, str]]:
@@ -127,7 +127,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
127
127
  if output:
128
128
  PrettyOutput.section("相关文件", OutputType.INFO)
129
129
  PrettyOutput.print(output, OutputType.INFO, lang="markdown")
130
-
130
+
131
131
  if len(related_files) > 0:
132
132
  # Ask the user if they need to adjust the file list
133
133
  if user_confirm("是否需要调整文件列表?", False):
@@ -139,7 +139,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
139
139
  selected_files = [related_files[i] for i in selected_indices]
140
140
  else:
141
141
  PrettyOutput.print("没有有效的文件被选择, 保持当前选择", OutputType.WARNING)
142
-
142
+
143
143
  tips = ""
144
144
  # Ask if they need to supplement files
145
145
  if user_confirm("是否需要补充其他文件?", False):
@@ -154,23 +154,23 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
154
154
  file_path = session.prompt(">>> ").strip()
155
155
  except KeyboardInterrupt:
156
156
  break
157
-
157
+
158
158
  if not file_path:
159
159
  break
160
-
160
+
161
161
  # Process wildcard matching
162
162
  if '*' in file_path or '?' in file_path:
163
163
  matches = _fuzzy_match_files(root_dir, file_path)
164
164
  if not matches:
165
165
  PrettyOutput.print("没有找到匹配的文件", OutputType.WARNING)
166
166
  continue
167
-
167
+
168
168
  # Display matching files
169
169
  tips = "找到以下匹配的文件:"
170
170
  for i, path in enumerate(matches, 1):
171
171
  tips += f"\n[{i}] {path}"
172
172
  PrettyOutput.print(tips, OutputType.INFO)
173
-
173
+
174
174
  # Let the user select
175
175
  numbers = get_single_line_input("请选择要添加的文件编号(支持: 1,3-6格式, 按回车选择所有)").strip()
176
176
  if numbers:
@@ -182,7 +182,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
182
182
  paths_to_add = matches
183
183
  else:
184
184
  paths_to_add = [file_path]
185
-
185
+
186
186
  # Add selected files
187
187
  tips = "添加以下文件:"
188
188
  for path in paths_to_add:
@@ -190,7 +190,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
190
190
  if not os.path.isfile(full_path):
191
191
  tips += f"\n文件不存在: {path}"
192
192
  continue
193
-
193
+
194
194
  try:
195
195
  selected_files.append({"file": path, "reason": "I Added"})
196
196
  tips += f"\n文件已添加: {path}"