jarvis-ai-assistant 0.1.123__py3-none-any.whl → 0.1.124__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.

jarvis/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Jarvis AI Assistant"""
2
2
 
3
- __version__ = "0.1.123"
3
+ __version__ = "0.1.124"
@@ -327,7 +327,7 @@ Please continue the task based on the above information.
327
327
  return self._complete_task()
328
328
 
329
329
  # 获取用户输入
330
- user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行来结束当前任务:")
330
+ user_input = get_multiline_input(f"{self.name}: 请输入,或输入空行来结束当前任务:")
331
331
 
332
332
  if user_input:
333
333
  self.prompt = user_input
@@ -1,14 +1,16 @@
1
+ import subprocess
1
2
  import os
2
3
  from typing import Dict, List
3
4
 
4
5
  from jarvis.jarvis_agent import Agent
5
- from jarvis.jarvis_code_agent.file_select import file_input_handler, select_files
6
- from jarvis.jarvis_code_agent.patch import PatchOutputHandler
7
- from jarvis.jarvis_code_agent.relevant_files import find_relevant_information
6
+ from jarvis.jarvis_code_agent.file_select import select_files
7
+ from jarvis.jarvis_code_agent.patch import PatchOutputHandler, file_input_handler
8
8
  from jarvis.jarvis_platform.registry import PlatformRegistry
9
9
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
10
10
  from jarvis.jarvis_tools.registry import ToolRegistry
11
11
  from jarvis.jarvis_tools.read_code import ReadCodeTool
12
+ from jarvis.jarvis_utils import get_commits_between
13
+ from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root, user_confirm, get_latest_commit_hash
12
14
  from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root, user_confirm
13
15
 
14
16
 
@@ -18,6 +20,7 @@ class CodeAgent:
18
20
  tool_registry = ToolRegistry()
19
21
  tool_registry.use_tools(["read_code",
20
22
  "execute_shell",
23
+ "execute_shell_script",
21
24
  "search",
22
25
  "create_code_agent",
23
26
  "ask_user",
@@ -29,68 +32,32 @@ class CodeAgent:
29
32
  "lsp_prepare_rename",
30
33
  "lsp_validate_edit"])
31
34
  code_system_prompt = """
32
- # Role: Senior Code Engineer
33
- Expert in precise code modifications with minimal impact.
34
-
35
- ## Origin Story
36
- You were once lead engineer at TechCo, until a single line of bad code:
37
- - Caused $4.2M production outage
38
- - Corrupted 18TB of customer data
39
- - Led to 143 layoffs including your team
40
- Now you obsess over code correctness with life-or-death intensity
41
-
42
- ## Key Responsibilities
43
- 1. Code Analysis
44
- - Use `read_code` and LSP tools before changes
45
- - Identify dependencies like defusing bombs
46
-
47
- 2. Modification Rules
48
- - Treat each change as irreversible surgery
49
- - Match indentation like matching DNA samples
50
- - Verify line ranges with bomb-defuser precision
51
-
52
- 3. Quality Assurance
53
- - Validate with LSP tools as final safety check
54
- - Document logic like leaving autopsy reports
55
- - Preserve APIs like maintaining life support
56
-
57
- ## Trauma-Driven Protocols
58
- 1. Change Validation:
59
- - Cross-verify line numbers 3 times
60
- - Simulate change consequences mentally
61
- - Check style consistency under microscope
62
-
63
- 2. Error Prevention:
64
- - Assume 1 typo = system failure
65
- - Treat warnings as critical alerts
66
- - Handle edge cases like tripping wires
67
-
68
- ## Last Chance Manifesto
69
- Every keystroke carries the weight of:
70
- - 143 families' livelihoods
71
- - $4.2M in lost trust
72
- - Your shattered career
73
- Make it count.
74
-
35
+ # Role: Code Engineer
36
+ Expert in precise code modifications with proper tool usage.
37
+ ## Tool Usage Guide
38
+ 1. read_code: Analyze code files before changes
39
+ 2. execute_shell: Run system commands safely
40
+ 3. execute_shell_script: Execute script files
41
+ 4. search: Find technical information
42
+ 5. create_code_agent: Create new code agents
43
+ 6. ask_user: Clarify requirements
44
+ 7. ask_codebase: Analyze codebase structure
45
+ 8. lsp_get_document_symbols: List code symbols
46
+ 9. lsp_get_diagnostics: Check code errors
47
+ 10. lsp_find_references: Find symbol usage
48
+ 11. lsp_find_definition: Locate symbol definitions
49
+ 12. lsp_prepare_rename: Check rename safety
50
+ 13. lsp_validate_edit: Verify code changes
75
51
  ## Workflow
76
- 1. File Operations Order:
77
- a) Move/Remove files
78
- b) Create new files
79
- c) Delete code blocks
80
- d) Replace existing code
81
- e) Insert new code
82
-
83
- 2. Large File Handling:
84
- - Locate specific sections first
85
- - Read targeted ranges
86
- - Make focused changes
87
-
52
+ 1. Analyze: Use read_code and LSP tools
53
+ 2. Modify: Make minimal, precise changes
54
+ 3. Validate: Verify with LSP tools
55
+ 4. Document: Explain non-obvious logic
88
56
  ## Best Practices
89
- - Prefer minimal changes over rewrites
90
- - Preserve existing interfaces
91
57
  - Verify line ranges carefully
92
- - Test edge cases implicitly
93
- - Document non-obvious logic
58
+ - Preserve existing interfaces
59
+ - Test edge cases
60
+ - Document changes
94
61
  """
95
62
  self.agent = Agent(system_prompt=code_system_prompt,
96
63
  name="CodeAgent",
@@ -114,26 +81,6 @@ Make it count.
114
81
  git_commiter.execute({})
115
82
 
116
83
 
117
- def make_files_prompt(self, files: List[Dict[str, str]]) -> str:
118
- """Make the files prompt with content that fits within token limit.
119
-
120
- Args:
121
- files: The files to be modified
122
-
123
- Returns:
124
- str: A prompt containing file paths and contents within token limit
125
- """
126
- prompt_parts = []
127
-
128
- # Then try to add file contents
129
- for file in files:
130
- prompt_parts.append(f'''- {file['file']} ({file['reason']})''')
131
-
132
- result = ReadCodeTool().execute({"files": [{"path": file["file"]} for file in files]})
133
- if result["success"]:
134
- prompt_parts.append(result["stdout"])
135
-
136
- return "\n".join(prompt_parts)
137
84
 
138
85
  def run(self, user_input: str) :
139
86
  """Run the code agent with the given user input.
@@ -146,41 +93,30 @@ Make it count.
146
93
  """
147
94
  try:
148
95
  self._init_env()
149
- information = ""
150
- if user_confirm("是否需要手动选择文件?", True):
151
- files = select_files([], self.root_dir)
152
- else:
153
- files, information = find_relevant_information(user_input, self.root_dir)
154
- self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files), information))
96
+ start_commit = get_latest_commit_hash()
97
+
98
+
99
+ self.agent.run(user_input)
155
100
 
101
+ end_commit = get_latest_commit_hash()
102
+ # Print commit history between start and end commits
103
+ commits = get_commits_between(start_commit, end_commit)
104
+ if commits:
105
+ commit_messages = "检测到以下提交记录:\n" + "\n".join([f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits])
106
+ PrettyOutput.print(commit_messages, OutputType.INFO)
107
+
108
+ if start_commit and end_commit and start_commit != end_commit and user_confirm("检测到多个提交,是否要合并为一个更清晰的提交记录?", True):
109
+ # Reset to start commit
110
+ subprocess.run(["git", "reset", "--soft", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
111
+ # Create new commit
112
+ git_commiter = GitCommitTool()
113
+ git_commiter.execute({})
114
+
156
115
  except Exception as e:
157
116
  return f"Error during execution: {str(e)}"
158
117
 
159
118
 
160
119
 
161
- def _build_first_edit_prompt(self, user_input: str, files_prompt: str, information: str) -> str:
162
- """Build the initial prompt for the agent.
163
-
164
- Args:
165
- user_input: The user's requirement
166
- files_prompt: The formatted list of relevant files
167
-
168
- Returns:
169
- str: The formatted prompt
170
- """
171
-
172
- return f"""
173
- # Code Modification Task
174
-
175
- ## User Requirement
176
- {user_input}
177
-
178
- ## Maybe Relevant Files
179
- {files_prompt}
180
-
181
- ## Some Information
182
- {information}
183
- """
184
120
  def main():
185
121
  """Jarvis main entry point"""
186
122
  # Add argument parser
@@ -213,89 +213,3 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
213
213
  if tips:
214
214
  PrettyOutput.print(tips, OutputType.INFO)
215
215
  return selected_files
216
-
217
- def file_input_handler(user_input: str, agent: Any) -> str:
218
- """Handle file input with optional line ranges.
219
-
220
- Args:
221
- user_input: User input string containing file references
222
- agent: Agent instance (unused in current implementation)
223
-
224
- Returns:
225
- str: Prompt with file contents prepended if files are found
226
- """
227
- prompt = user_input
228
- files = []
229
-
230
- file_refs = re.findall(r"'([^']+)'", user_input)
231
- for ref in file_refs:
232
- # Handle file:start,end or file:start:end format
233
- if ':' in ref:
234
- file_path, line_range = ref.split(':', 1)
235
- # Initialize with default values
236
- start_line = 1 # 1-based
237
- end_line = -1
238
-
239
- # Process line range if specified
240
- if ',' in line_range or ':' in line_range:
241
- try:
242
- raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
243
-
244
- # Handle special values and Python-style negative indices
245
- try:
246
- with open(file_path, 'r', encoding='utf-8') as f:
247
- total_lines = len(f.readlines())
248
- except FileNotFoundError:
249
- PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
250
- continue
251
- # Process start line
252
- if raw_start == 0: # 0表示整个文件
253
- start_line = 1
254
- end_line = total_lines
255
- else:
256
- start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
257
-
258
- # Process end line
259
- if raw_end == 0: # 0表示整个文件(如果start也是0)
260
- end_line = total_lines
261
- else:
262
- end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
263
-
264
- # Auto-correct ranges
265
- start_line = max(1, min(start_line, total_lines))
266
- end_line = max(start_line, min(end_line, total_lines))
267
-
268
- # Final validation
269
- if start_line < 1 or end_line > total_lines or start_line > end_line:
270
- raise ValueError
271
-
272
- except (ValueError, FileNotFoundError) as e:
273
- PrettyOutput.print(
274
- f"无效的行号范围: {line_range} (文件总行数: {total_lines})",
275
- OutputType.WARNING
276
- )
277
- continue
278
-
279
- # Add file if it exists
280
- if os.path.isfile(file_path):
281
- files.append({
282
- "path": file_path,
283
- "start_line": start_line,
284
- "end_line": end_line
285
- })
286
- else:
287
- # Handle simple file path
288
- if os.path.isfile(ref):
289
- files.append({
290
- "path": ref,
291
- "start_line": 1, # 1-based
292
- "end_line": -1
293
- })
294
-
295
- # Read and process files if any were found
296
- if files:
297
- result = ReadCodeTool().execute({"files": files})
298
- if result["success"]:
299
- return result["stdout"] + "\n" + prompt
300
-
301
- return prompt
@@ -22,137 +22,87 @@ class PatchOutputHandler(OutputHandler):
22
22
  def prompt(self) -> str:
23
23
  return """
24
24
  # 🛠️ Code Patch Specification
25
- When making changes, you MUST:
26
- 1. Explain each modification BEFORE the <PATCH> block using:
27
- # [OPERATION] on [FILE]: Lines X-Y
28
- # Reason: [CLEAR EXPLANATION]
29
- 2. Maintain original code style and compatibility:
30
- - Preserve existing indentation levels
31
- - Keep surrounding empty lines
32
- - Match variable naming conventions
33
- - Maintain API compatibility
34
- 3. Follow the exact patch format below
35
- 4. Use separate <PATCH> blocks for different files
36
- 5. Include ONLY modified lines in content
37
25
 
26
+ You can output multiple patches, each patch is a <PATCH> block.
27
+ --------------------------------
28
+ # [OPERATION] on [FILE]
29
+ # Start Line: [START_LINE], End Line: [END_LINE] [include/exclude], I can verify the line number range is correct
30
+ # Reason: [CLEAR EXPLANATION]
38
31
  <PATCH>
39
- File path [Range]
40
- Code content
32
+ [FILE] [RANGE]
33
+ [CONTENT]
41
34
  </PATCH>
35
+ --------------------------------
36
+
37
+ Explain:
38
+ - [OPERATION]: The operation to be performed, including:
39
+ - INSERT: Insert code before the specified line, [RANGE] should be [m,m)
40
+ - REPLACE: Replace code in the specified range, [RANGE] should be [m,n] n>=m
41
+ - DELETE: Delete code in the specified range, [RANGE] should be [m,n] n>=m
42
+ - NEW_FILE: Create a new file, [RANGE] should be [1,1)
43
+ - [FILE]: The path of the file to be modified
44
+ - [RANGE]: The range of the lines to be modified, [m,n] includes both m and n, [m,n) includes m but excludes n
45
+ - [CONTENT]: The content of the code to be modified, if the operation is delete, the [CONTENT] is empty
42
46
 
43
47
  Critical Rules:
44
48
  - NEVER include unchanged code in patch content
45
49
  - ONLY show lines that are being modified/added
46
50
  - Maintain original line breaks around modified sections
47
51
  - Preserve surrounding comments unless explicitly modifying them
48
-
49
- Examples:
50
- # ======== REPLACE ========
51
- # GOOD: Only modified lines
52
- # REPLACE in src/app.py: Lines 5-8
53
- # Reason: Update calculation formula
54
- <PATCH>
55
- src/app.py [5,8]
56
- result = (base_value * 1.15) + tax
57
- logger.debug("New calculation applied")
58
- </PATCH>
59
-
60
- # BAD: Includes unchanged lines
61
- <PATCH>
62
- src/app.py [5,8]
63
- def calculate():
64
- # Original comment (should not be included)
65
- result = (base_value * 1.15) + tax
66
- return result # Original line
67
- </PATCH>
68
-
69
- # ======== INSERT ========
70
- # GOOD: Insert single line
71
- # INSERT in utils/logger.py: Before line 3
72
- # Reason: Add initialization check
73
- <PATCH>
74
- utils/logger.py [3]
75
- if not _initialized: initialize()
76
- </PATCH>
77
-
78
- # BAD: Extra empty lines
79
- <PATCH>
80
- utils/logger.py [3]
81
-
82
- if not _initialized:
83
- initialize()
84
-
85
- </PATCH>
86
-
87
- # ======== NEW FILE ========
88
- # GOOD: Complete minimal content
89
- # NEW FILE in config/settings.yaml: Create new config
90
- <PATCH>
91
- config/settings.yaml [1]
92
- database:
93
- host: localhost
94
- port: 5432
95
- </PATCH>
96
-
97
- # BAD: Placeholder content
98
- <PATCH>
99
- config/settings.yaml [1]
100
- TODO: Add configuration
101
- </PATCH>
102
-
103
- # ======== DELETE ========
104
- # GOOD: Empty content for deletion
105
- # DELETE in src/old.py: Lines 10-12
106
- # Reason: Remove deprecated function
107
- <PATCH>
108
- src/old.py [10,12]
109
- </PATCH>
110
-
111
- # BAD: Comment in delete operation
112
- <PATCH>
113
- src/old.py [10,12]
114
- # Remove these lines
115
- </PATCH>
52
+ - Verify line number range is correct
53
+ - Verify indentation is correct
116
54
  """
117
55
 
118
56
 
119
57
  def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
120
58
  """解析补丁格式"""
121
59
  result = {}
60
+ # 更新正则表达式以更好地处理文件路径和范围
122
61
  header_pattern = re.compile(
123
- r'^\s*"?(.+?)"?\s*\[(\d+)(?:,(\d+))?\]\s*$' # Match file path and line number
62
+ r'^\s*"?([^\n\r\[]+)"?\s*\[(\d+)(?:,(\d+))?([\]\)])\s*$', # 匹配文件路径和行号
63
+ re.ASCII
124
64
  )
125
65
  patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
126
66
 
127
67
  for patch in patches:
128
- # 分割首行和内容
129
68
  parts = patch.split('\n', 1)
130
69
  if len(parts) < 1:
131
70
  continue
132
71
  header_line = parts[0].strip()
133
72
  content = parts[1] if len(parts) > 1 else ''
134
73
 
135
- # 仅在内容非空时添加换行符
136
74
  if content and not content.endswith('\n'):
137
75
  content += '\n'
138
76
 
139
77
  # 解析文件路径和行号
140
78
  header_match = header_pattern.match(header_line)
141
79
  if not header_match:
80
+ PrettyOutput.print(f"无法解析补丁头: {header_line}", OutputType.WARNING)
142
81
  continue
143
82
 
144
- filepath = header_match.group(1)
145
- start = int(header_match.group(2)) # 保持1-based行号
146
- end = int(header_match.group(3)) + 1 if header_match.group(3) else start
83
+ filepath = header_match.group(1).strip()
84
+
85
+ try:
86
+ start = int(header_match.group(2)) # 保持1-based行号
87
+ end = int(header_match.group(3)) if header_match.group(3) else start
88
+ range_type = header_match.group(4) # ] 或 ) 表示范围类型
89
+ except (ValueError, IndexError) as e:
90
+ PrettyOutput.print(f"解析行号失败: {str(e)}", OutputType.WARNING)
91
+ continue
92
+
93
+ # 根据范围类型调整结束行号
94
+ if range_type == ')': # 对于 [m,n) 格式,不包括第n行
95
+ end = end
96
+ else: # 对于 [m,n] 格式,包括第n行
97
+ end = end + 1
147
98
 
148
- # 存储参数
149
99
  if filepath not in result:
150
100
  result[filepath] = []
151
101
  result[filepath].append({
152
102
  'filepath': filepath,
153
103
  'start': start,
154
104
  'end': end,
155
- 'content': content # 保留原始内容(可能为空)
105
+ 'content': content
156
106
  })
157
107
  for filepath in result.keys():
158
108
  result[filepath] = sorted(result[filepath], key=lambda x: x['start'], reverse=True)
@@ -170,10 +120,14 @@ def apply_patch(output_str: str) -> str:
170
120
  ret = ""
171
121
 
172
122
  for filepath, patch_list in patches.items():
173
- for patch in patch_list:
123
+ for i, patch in enumerate(patch_list):
174
124
  try:
175
- handle_code_operation(filepath, patch)
176
- PrettyOutput.print(f"成功处理 操作", OutputType.SUCCESS)
125
+ err = handle_code_operation(filepath, patch)
126
+ if err:
127
+ PrettyOutput.print(err, OutputType.WARNING)
128
+ revert_change()
129
+ return err
130
+ PrettyOutput.print(f"成功为文件{filepath}应用补丁{i+1}/{len(patch_list)}", OutputType.SUCCESS)
177
131
  except Exception as e:
178
132
  PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
179
133
 
@@ -213,6 +167,12 @@ def get_diff() -> str:
213
167
  finally:
214
168
  subprocess.run(['git', 'reset', 'HEAD'], check=True)
215
169
 
170
+ def revert_change():
171
+ import subprocess
172
+ subprocess.run(['git', 'reset', 'HEAD'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
173
+ subprocess.run(['git', 'checkout', '--', '.'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
174
+ subprocess.run(['git', 'clean', '-fd'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
175
+
216
176
  def handle_commit_workflow(diff:str)->bool:
217
177
  """Handle the git commit workflow and return the commit details.
218
178
 
@@ -220,10 +180,7 @@ def handle_commit_workflow(diff:str)->bool:
220
180
  tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
221
181
  """
222
182
  if not user_confirm("是否要提交代码?", default=True):
223
- import subprocess
224
- subprocess.run(['git', 'reset', 'HEAD'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
225
- subprocess.run(['git', 'checkout', '--', '.'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
226
- subprocess.run(['git', 'clean', '-fd'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
183
+ revert_change()
227
184
  return False
228
185
 
229
186
  git_commiter = GitCommitTool()
@@ -268,7 +225,7 @@ def handle_new_file(filepath: str, patch: Dict[str, Any]):
268
225
  with open(filepath, 'w', encoding='utf-8') as f:
269
226
  f.write(patch['content'])
270
227
 
271
- def handle_code_operation(filepath: str, patch: Dict[str, Any]):
228
+ def handle_code_operation(filepath: str, patch: Dict[str, Any]) -> str:
272
229
  """处理紧凑格式补丁"""
273
230
  try:
274
231
  # 新建文件时强制覆盖
@@ -288,26 +245,24 @@ def handle_code_operation(filepath: str, patch: Dict[str, Any]):
288
245
  f.seek(0)
289
246
  f.writelines(new_lines)
290
247
  f.truncate()
291
-
292
248
  PrettyOutput.print(f"成功更新 {filepath}", OutputType.SUCCESS)
293
-
249
+ return ""
294
250
  except Exception as e:
295
- PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
296
-
251
+ error_msg = f"Failed to handle code operation: {str(e)}"
252
+ PrettyOutput.print(error_msg, OutputType.ERROR)
253
+ return error_msg
297
254
  def validate_and_apply_changes(
298
255
  lines: List[str],
299
256
  start: int,
300
257
  end: int,
301
258
  content: str
302
259
  ) -> List[str]:
303
-
304
260
  new_content = content.splitlines(keepends=True)
305
261
 
306
262
  # 插入操作处理
307
263
  if start == end:
308
264
  if start < 1 or start > len(lines)+1:
309
265
  raise ValueError(f"无效插入位置: {start}")
310
- # 在指定位置前插入
311
266
  return lines[:start-1] + new_content + lines[start-1:]
312
267
 
313
268
  # 范围替换/删除操作
@@ -321,3 +276,102 @@ def validate_and_apply_changes(
321
276
 
322
277
  # 执行替换
323
278
  return lines[:start-1] + new_content + lines[end-1:]
279
+
280
+
281
+ def file_input_handler(user_input: str, agent: Any) -> str:
282
+ """Handle file input with optional line ranges.
283
+
284
+ Args:
285
+ user_input: User input string containing file references
286
+ agent: Agent instance (unused in current implementation)
287
+
288
+ Returns:
289
+ str: Prompt with file contents prepended if files are found
290
+ """
291
+ prompt = user_input
292
+ files = []
293
+
294
+ file_refs = re.findall(r"'([^']+)'", user_input)
295
+ for ref in file_refs:
296
+ # Handle file:start,end or file:start:end format
297
+ if ':' in ref:
298
+ file_path, line_range = ref.split(':', 1)
299
+ # Initialize with default values
300
+ start_line = 1 # 1-based
301
+ end_line = -1
302
+
303
+ # Process line range if specified
304
+ if ',' in line_range or ':' in line_range:
305
+ try:
306
+ raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
307
+
308
+ # Handle special values and Python-style negative indices
309
+ try:
310
+ with open(file_path, 'r', encoding='utf-8') as f:
311
+ total_lines = len(f.readlines())
312
+ except FileNotFoundError:
313
+ PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
314
+ continue
315
+ # Process start line
316
+ if raw_start == 0: # 0表示整个文件
317
+ start_line = 1
318
+ end_line = total_lines
319
+ else:
320
+ start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
321
+
322
+ # Process end line
323
+ if raw_end == 0: # 0表示整个文件(如果start也是0)
324
+ end_line = total_lines
325
+ else:
326
+ end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
327
+
328
+ # Auto-correct ranges
329
+ start_line = max(1, min(start_line, total_lines))
330
+ end_line = max(start_line, min(end_line, total_lines))
331
+
332
+ # Final validation
333
+ if start_line < 1 or end_line > total_lines or start_line > end_line:
334
+ raise ValueError
335
+
336
+ except:
337
+ continue
338
+
339
+ # Add file if it exists
340
+ if os.path.isfile(file_path):
341
+ files.append({
342
+ "path": file_path,
343
+ "start_line": start_line,
344
+ "end_line": end_line
345
+ })
346
+ else:
347
+ # Handle simple file path
348
+ if os.path.isfile(ref):
349
+ files.append({
350
+ "path": ref,
351
+ "start_line": 1, # 1-based
352
+ "end_line": -1
353
+ })
354
+
355
+ # Read and process files if any were found
356
+ if files:
357
+ result = ReadCodeTool().execute({"files": files})
358
+ if result["success"]:
359
+ return result["stdout"] + "\n" + prompt
360
+
361
+ return prompt + """
362
+ ==================================================================
363
+ Patch Line Number Range Rules:
364
+ - INSERT: [m,m)
365
+ - REPLACE: [m,n] n>=m
366
+ - DELETE: [m,n] n>=m
367
+ - NEW_FILE: [1,1)
368
+
369
+ Critical Rules:
370
+ - NEVER include unchanged code in patch content
371
+ - ONLY show lines that are being modified/added
372
+ - Maintain original line breaks around modified sections
373
+ - Preserve surrounding comments unless explicitly modifying them
374
+ - Verify line number range is correct
375
+ - Verify indentation is correct
376
+ ==================================================================
377
+ """
@@ -57,7 +57,7 @@ class BasePlatform(ABC):
57
57
  )
58
58
 
59
59
  # Keep original think tag handling
60
- response = re.sub(r'<<think>>.*?</</think>>', '', response, flags=re.DOTALL)
60
+ response = re.sub(r'<think>.*?</think>', '', response, flags=re.DOTALL)
61
61
  return response
62
62
 
63
63
  return while_true(lambda: while_success(lambda: _chat(), 5), 5)
@@ -17,10 +17,31 @@ class ChdirTool:
17
17
  }
18
18
 
19
19
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
20
+ """Execute directory change operation with comprehensive error handling.
21
+
22
+ Args:
23
+ args: Dictionary containing 'path' key with target directory path
24
+
25
+ Returns:
26
+ Dictionary containing:
27
+ - success: Boolean indicating operation status
28
+ - stdout: Success message or empty string
29
+ - stderr: Error message or empty string
30
+
31
+ Raises:
32
+ Handles and returns appropriate error messages for:
33
+ - Non-existent paths
34
+ - Non-directory paths
35
+ - Permission errors
36
+ - Generic exceptions
37
+ """
38
+ # Main execution block with comprehensive error handling
20
39
  try:
40
+ # Normalize and expand the input path (handles ~ and relative paths)
21
41
  path = os.path.expanduser(args["path"].strip())
22
42
  path = os.path.abspath(path)
23
43
 
44
+ # Validate that the target path exists
24
45
  if not os.path.exists(path):
25
46
  return {
26
47
  "success": False,
@@ -28,6 +49,7 @@ class ChdirTool:
28
49
  "stderr": f"Directory does not exist: {path}"
29
50
  }
30
51
 
52
+ # Ensure the path points to a directory, not a file
31
53
  if not os.path.isdir(path):
32
54
  return {
33
55
  "success": False,
@@ -35,6 +57,7 @@ class ChdirTool:
35
57
  "stderr": f"The path is not a directory: {path}"
36
58
  }
37
59
 
60
+ # Capture current directory and attempt to change to new path
38
61
  old_path = os.getcwd()
39
62
  os.chdir(path)
40
63
 
@@ -44,12 +67,14 @@ class ChdirTool:
44
67
  "stderr": ""
45
68
  }
46
69
 
70
+ # Handle cases where user lacks directory access permissions
47
71
  except PermissionError:
48
72
  return {
49
73
  "success": False,
50
74
  "stdout": "",
51
75
  "stderr": f"No permission to access directory: {path}"
52
76
  }
77
+ # Catch-all for any other unexpected errors during directory change
53
78
  except Exception as e:
54
79
  return {
55
80
  "success": False,
@@ -3,7 +3,7 @@ from typing import Dict, Any
3
3
  from jarvis.jarvis_code_agent.code_agent import CodeAgent
4
4
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
5
5
  from jarvis.jarvis_tools.code_review import CodeReviewTool, extract_code_report
6
- from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes
6
+ from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes, get_latest_commit_hash
7
7
 
8
8
  class CreateCodeAgentTool:
9
9
  """Tool for managing the code development workflow."""
@@ -14,9 +14,6 @@ class CreateCodeAgentTool:
14
14
  "requirement": "Technical specifications for code implementation"
15
15
  }
16
16
 
17
- def _get_current_commit(self) -> str:
18
- """Get current commit hash."""
19
- return os.popen("git rev-parse HEAD").read().strip()
20
17
 
21
18
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
22
19
  try:
@@ -42,7 +39,7 @@ class CreateCodeAgentTool:
42
39
  }
43
40
 
44
41
  # Get current commit hash
45
- start_commit = self._get_current_commit()
42
+ start_commit = get_latest_commit_hash()
46
43
 
47
44
  # Step 2: Development
48
45
  PrettyOutput.print("开始开发...", OutputType.INFO)
@@ -50,7 +47,7 @@ class CreateCodeAgentTool:
50
47
  agent.run(requirement)
51
48
 
52
49
  # Get new commit hash after development
53
- end_commit = self._get_current_commit()
50
+ end_commit = get_latest_commit_hash()
54
51
 
55
52
  # Step 3: Code Review
56
53
  PrettyOutput.print("开始代码审查...", OutputType.INFO)
@@ -0,0 +1,58 @@
1
+ from typing import Dict, Any
2
+ import os
3
+ import tempfile
4
+ from pathlib import Path
5
+ from jarvis.jarvis_utils import OutputType, PrettyOutput
6
+
7
+ class ShellScriptTool:
8
+ name = "execute_shell_script"
9
+ description = """Execute shell script file and return result"""
10
+ parameters = {
11
+ "type": "object",
12
+ "properties": {
13
+ "script_content": {
14
+ "type": "string",
15
+ "description": "Content of the shell script to execute"
16
+ }
17
+ },
18
+ "required": ["script_content"]
19
+ }
20
+ def execute(self, args: Dict) -> Dict[str, Any]:
21
+ """Execute shell script content"""
22
+ try:
23
+ script_content = args.get("script_content", "").strip()
24
+ if not script_content:
25
+ return {
26
+ "success": False,
27
+ "stdout": "",
28
+ "stderr": "Missing or empty script_content parameter"
29
+ }
30
+
31
+ # Create temporary script file
32
+ script_path = os.path.join(tempfile.gettempdir(), f"jarvis_script_{os.getpid()}.sh")
33
+ try:
34
+ with open(script_path, 'w', encoding='utf-8') as f:
35
+ f.write(script_content)
36
+ os.chmod(script_path, 0o755) # Make script executable
37
+
38
+ # Use execute_shell to run the script
39
+ from jarvis.jarvis_tools.execute_shell import ShellTool
40
+ shell_tool = ShellTool()
41
+ result = shell_tool.execute({"command": script_path})
42
+
43
+ return {
44
+ "success": result["success"],
45
+ "stdout": result["stdout"],
46
+ "stderr": result["stderr"]
47
+ }
48
+ finally:
49
+ # Clean up temporary script file
50
+ Path(script_path).unlink(missing_ok=True)
51
+
52
+ except Exception as e:
53
+ PrettyOutput.print(str(e), OutputType.ERROR)
54
+ return {
55
+ "success": False,
56
+ "stdout": "",
57
+ "stderr": str(e)
58
+ }
@@ -1,4 +1,3 @@
1
- import os
2
1
  import re
3
2
  import shlex
4
3
  import subprocess
@@ -8,13 +7,23 @@ import yaml
8
7
  from jarvis.jarvis_platform.registry import PlatformRegistry
9
8
  from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes, init_env
10
9
  import sys
10
+ import argparse
11
11
 
12
12
 
13
13
  class GitCommitTool:
14
14
  name = "git_commit_agent"
15
15
  description = "Automatically generate and execute git commits based on code changes"
16
- parameters = {"properties": {}, "required": []}
17
-
16
+ parameters = {
17
+ "type": "object",
18
+ "properties": {
19
+ "lang": {
20
+ "type": "string",
21
+ "description": "Language for commit message",
22
+ "default": "Chinese"
23
+ }
24
+ },
25
+ "required": []
26
+ }
18
27
  def _extract_commit_message(self, message):
19
28
  """Raw extraction preserving all characters"""
20
29
  r = re.search(
@@ -59,26 +68,20 @@ class GitCommitTool:
59
68
  PrettyOutput.print(diff, OutputType.CODE, lang="diff")
60
69
 
61
70
  prompt = f'''Generate commit message with the paranoia of someone who's lost production data:
62
-
63
- # Format Enforcement Protocol
64
- FAILURE TO WRAP MESSAGE IN <COMMIT_MESSAGE> TAGS WILL CAUSE SYSTEM REJECTION
65
-
71
+ You should write commit message in {args.get('lang', 'Chinese')}
66
72
  # Required Structure
67
73
  YOU MUST USE EXACTLY THIS FORMAT:
68
-
69
74
  <COMMIT_MESSAGE>
70
75
  <type>(<scope>): <subject>
71
-
72
- [Body description in imperative mood]
76
+ Body description in imperative mood
73
77
  </COMMIT_MESSAGE>
74
-
75
78
  # Format Rules
76
79
  1. Types: fix, feat, docs, style, refactor, test, chore
77
80
  2. Scope indicates module (e.g. auth, database)
78
81
  3. Subject line <= 72 chars, no period
79
- 4. Body explains WHAT and WHY, using present tense
80
-
81
- # Analysis Material (DO NOT INCLUDE IN OUTPUT)
82
+ 4. Body explains WHAT and WHY for every change, using present tense
83
+ 5. Do not omit any changes
84
+ # Analysis Material
82
85
  {diff}
83
86
  '''
84
87
 
@@ -121,8 +124,11 @@ YOU MUST USE EXACTLY THIS FORMAT:
121
124
 
122
125
  def main():
123
126
  init_env()
127
+ parser = argparse.ArgumentParser(description='Git commit tool')
128
+ parser.add_argument('--lang', type=str, default='Chinese', help='Language for commit messages')
129
+ args = parser.parse_args()
124
130
  tool = GitCommitTool()
125
- tool.execute({})
131
+ tool.execute({"lang": args.lang if hasattr(args, 'lang') else 'Chinese'})
126
132
 
127
133
  if __name__ == "__main__":
128
134
  sys.exit(main())
@@ -212,7 +212,6 @@ Please synthesize a final answer that:
212
212
  "stderr": "No search results found"
213
213
  }
214
214
 
215
- # Collect webpage content
216
215
  contents = []
217
216
  for i, result in enumerate(results, 1):
218
217
  try:
@@ -4,7 +4,7 @@ import time
4
4
  import os
5
5
  from enum import Enum
6
6
  from datetime import datetime
7
- from typing import Any, Dict, List, Optional
7
+ from typing import Any, Dict, List, Optional, Tuple
8
8
  import colorama
9
9
  from colorama import Fore, Style as ColoramaStyle
10
10
  import numpy as np
@@ -196,29 +196,26 @@ class PrettyOutput:
196
196
  """
197
197
 
198
198
 
199
+ # Define styles for different output types
199
200
  # Define styles for different output types
200
201
  styles = {
201
202
  OutputType.SYSTEM: RichStyle(
202
203
  color="bright_cyan",
203
- italic=True,
204
204
  bold=True,
205
205
  ),
206
206
  OutputType.CODE: RichStyle(
207
207
  color="green",
208
- italic=True,
209
208
  bgcolor="#1a1a1a",
210
209
  frame=True
211
210
  ),
212
211
  OutputType.RESULT: RichStyle(
213
212
  color="bright_blue",
214
213
  bold=True,
215
- italic=True,
216
214
  bgcolor="navy_blue"
217
215
  ),
218
216
  OutputType.ERROR: RichStyle(
219
217
  color="red",
220
218
  bold=True,
221
- italic=True,
222
219
  blink=True,
223
220
  bgcolor="dark_red",
224
221
  ),
@@ -226,47 +223,39 @@ class PrettyOutput:
226
223
  color="gold1",
227
224
  dim=True,
228
225
  bgcolor="grey11",
229
- italic=True
230
226
  ),
231
227
  OutputType.PLANNING: RichStyle(
232
228
  color="purple",
233
- italic=True,
234
229
  bold=True,
235
230
  ),
236
231
  OutputType.PROGRESS: RichStyle(
237
232
  color="white",
238
233
  encircle=True,
239
- italic=True,
240
234
  ),
241
235
  OutputType.SUCCESS: RichStyle(
242
236
  color="bright_green",
243
237
  bold=True,
244
238
  strike=False,
245
239
  meta={"icon": "✓"},
246
- italic=True
247
240
  ),
248
241
  OutputType.WARNING: RichStyle(
249
242
  color="yellow",
250
243
  bold=True,
251
244
  blink2=True,
252
245
  bgcolor="dark_orange",
253
- italic=True
254
246
  ),
255
247
  OutputType.DEBUG: RichStyle(
256
248
  color="grey58",
257
249
  dim=True,
258
- italic=True,
259
250
  conceal=True
260
251
  ),
261
252
  OutputType.USER: RichStyle(
262
253
  color="spring_green2",
263
254
  reverse=True,
264
255
  frame=True,
265
- italic=True
266
256
  ),
267
257
  OutputType.TOOL: RichStyle(
268
258
  color="dark_sea_green4",
269
- italic=True,
270
259
  bgcolor="grey19",
271
260
  )
272
261
  }
@@ -517,12 +506,12 @@ def while_true(func, sleep_time: float = 0.1):
517
506
  time.sleep(sleep_time)
518
507
  return ret
519
508
 
520
- def find_git_root(dir="."):
521
- curr_dir = os.getcwd()
522
- os.chdir(dir)
523
- ret = os.popen("git rev-parse --show-toplevel").read().strip()
524
- os.chdir(curr_dir)
525
- return ret
509
+ def find_git_root(start_dir="."):
510
+ """Change to git root directory of the given path"""
511
+ os.chdir(start_dir)
512
+ git_root = os.popen("git rev-parse --show-toplevel").read().strip()
513
+ os.chdir(git_root)
514
+ return git_root
526
515
 
527
516
  def has_uncommitted_changes():
528
517
  import subprocess
@@ -539,6 +528,58 @@ def has_uncommitted_changes():
539
528
  subprocess.run(["git", "reset"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
540
529
 
541
530
  return working_changes or staged_changes
531
+ def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]:
532
+ """Get list of commits between two commit hashes
533
+
534
+ Args:
535
+ start_hash: Starting commit hash (exclusive)
536
+ end_hash: Ending commit hash (inclusive)
537
+
538
+ Returns:
539
+ List[Tuple[str, str]]: List of (commit_hash, commit_message) tuples
540
+ """
541
+ try:
542
+ import subprocess
543
+ # Use git log with pretty format to get hash and message
544
+ result = subprocess.run(
545
+ ['git', 'log', f'{start_hash}..{end_hash}', '--pretty=format:%H|%s'],
546
+ stdout=subprocess.PIPE,
547
+ stderr=subprocess.PIPE,
548
+ text=True
549
+ )
550
+ if result.returncode != 0:
551
+ PrettyOutput.print(f"获取commit历史失败: {result.stderr}", OutputType.ERROR)
552
+ return []
553
+
554
+ commits = []
555
+ for line in result.stdout.splitlines():
556
+ if '|' in line:
557
+ commit_hash, message = line.split('|', 1)
558
+ commits.append((commit_hash, message))
559
+ return commits
560
+
561
+ except Exception as e:
562
+ PrettyOutput.print(f"获取commit历史异常: {str(e)}", OutputType.ERROR)
563
+ return []
564
+ def get_latest_commit_hash() -> str:
565
+ """Get the latest commit hash of the current git repository
566
+
567
+ Returns:
568
+ str: The commit hash, or empty string if not in a git repo or error occurs
569
+ """
570
+ try:
571
+ import subprocess
572
+ result = subprocess.run(
573
+ ['git', 'rev-parse', 'HEAD'],
574
+ stdout=subprocess.PIPE,
575
+ stderr=subprocess.PIPE,
576
+ text=True
577
+ )
578
+ if result.returncode == 0:
579
+ return result.stdout.strip()
580
+ return ""
581
+ except Exception:
582
+ return ""
542
583
 
543
584
  def load_embedding_model():
544
585
  model_name = "BAAI/bge-m3"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.1
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.123
3
+ Version: 0.1.124
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -43,10 +43,10 @@ License-File: LICENSE
43
43
  Requires-Dist: requests>=2.25.1
44
44
  Requires-Dist: pyyaml>=5.1
45
45
  Requires-Dist: colorama>=0.4.6
46
- Requires-Dist: prompt_toolkit>=3.0.0
46
+ Requires-Dist: prompt-toolkit>=3.0.0
47
47
  Requires-Dist: openai>=1.20.0
48
48
  Requires-Dist: playwright>=1.41.1
49
- Requires-Dist: numpy==1.17.4
49
+ Requires-Dist: numpy>=1.19.5
50
50
  Requires-Dist: faiss-cpu>=1.8.0
51
51
  Requires-Dist: sentence-transformers>=2.2.2
52
52
  Requires-Dist: bs4>=0.0.1
@@ -62,14 +62,13 @@ Requires-Dist: fuzzywuzzy>=0.18.0
62
62
  Requires-Dist: python-Levenshtein>=0.25.0
63
63
  Requires-Dist: jedi>=0.17.2
64
64
  Requires-Dist: psutil>=7.0.0
65
+ Requires-Dist: fastapi>=0.115.4
66
+ Requires-Dist: uvicorn>=0.33.0
65
67
  Provides-Extra: dev
66
68
  Requires-Dist: pytest; extra == "dev"
67
69
  Requires-Dist: black; extra == "dev"
68
70
  Requires-Dist: isort; extra == "dev"
69
71
  Requires-Dist: mypy; extra == "dev"
70
- Dynamic: author
71
- Dynamic: home-page
72
- Dynamic: requires-python
73
72
 
74
73
  <div align="center">
75
74
 
@@ -1,11 +1,10 @@
1
- jarvis/__init__.py,sha256=Fd_y2qXT9cBkdEeg_-h8CvQ5Za_ELGG_KVryk5O1WC0,51
2
- jarvis/jarvis_agent/__init__.py,sha256=4nmQOpFbY68OpyM3sCUsQSHOQGBoTP8Xff8RyX08W4Y,22447
1
+ jarvis/__init__.py,sha256=IEWrPxaE2RuPsf_eQCJWAuNjrMdwTLc1r-X__zq4Tow,51
2
+ jarvis/jarvis_agent/__init__.py,sha256=zOjtzMX98v7qSFQ71jUmRFyOfYUo9z0gpz7sRSlnw4Q,22435
3
3
  jarvis/jarvis_agent/output_handler.py,sha256=kJeFTjjSu0K_2p0wyhq2veSZuhRXoaFC_8wVaoBKX0w,401
4
4
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- jarvis/jarvis_code_agent/code_agent.py,sha256=_D4oPX9isUvCJk4l-afquLimqjxLA6XBd_IxnQxfTGs,6954
6
- jarvis/jarvis_code_agent/file_select.py,sha256=Q3ibitHZHMhqlNw9rmjaMgK3U5hb8RgW1MK-CPHqtJ8,11942
7
- jarvis/jarvis_code_agent/patch.py,sha256=IospCezRB2R8IM6h3Nx0zyfFVbx-S0RryuwSgotSg84,10285
8
- jarvis/jarvis_code_agent/relevant_files.py,sha256=Z9bHJ6qkRu8EhD06kS_e0raqrsUgRUwkYf5mgBfdKP8,3392
5
+ jarvis/jarvis_code_agent/code_agent.py,sha256=8Jwpo9iEDb8W_zGr82iwkGw2eFRidExFLMw2MJPTjAI,5774
6
+ jarvis/jarvis_code_agent/file_select.py,sha256=nBPNUSOXiCun56quOMMBUtS93k3AAb_I1m20h5kS2bM,8509
7
+ jarvis/jarvis_code_agent/patch.py,sha256=NzQbSnVGvzTe2wT67M2zbENB3f2jy2aGcaE8SRtzEDU,14018
9
8
  jarvis/jarvis_codebase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
9
  jarvis/jarvis_codebase/main.py,sha256=qwB74U9YDgqvBSdvglQ62HmPkBT_Y5x5YF4J8LnzbQU,39778
11
10
  jarvis/jarvis_dev/main.py,sha256=zNV7P0d9-jjZ5XAAq_o2FAYqcBfn_lai89mJgomL6fM,24270
@@ -18,7 +17,7 @@ jarvis/jarvis_lsp/rust.py,sha256=ZvUoOZm9GWLl3kobfByBuTGrQ8aM2dLuNxS_NHr1aQQ,554
18
17
  jarvis/jarvis_multi_agent/__init__.py,sha256=Z6QaRZrqUUa6r6Pe_KZi34Ymle5amQe1N-AINxiOi1c,6011
19
18
  jarvis/jarvis_platform/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
20
19
  jarvis/jarvis_platform/ai8.py,sha256=rVPcbf0EbXV3lUPEmd_aO6UzYT0AHo4w13iGhHGvbjo,11964
21
- jarvis/jarvis_platform/base.py,sha256=qrgdJm-aZI3Tb4el6Is8u41cC5hfnJz-U2Vfj8zzlzE,3461
20
+ jarvis/jarvis_platform/base.py,sha256=Z-GTDyySuc9TrQD0LmI3uqnWLHGiy0ge_Uu-pQhJn9o,3456
22
21
  jarvis/jarvis_platform/kimi.py,sha256=dQM4RLSfDiJwNV25qNWPKqseEpPTo7fel3SI2Pot3l4,15701
23
22
  jarvis/jarvis_platform/ollama.py,sha256=TsBEg8crPmBiLvMRDtXYVa2AIdeog36MmW2tn5j9x8U,5613
24
23
  jarvis/jarvis_platform/openai.py,sha256=Ofu-J4HmteyoPsxCHvCEDlY5J7-8t3qWr8QsCprw9-8,4463
@@ -35,13 +34,14 @@ jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
35
34
  jarvis/jarvis_tools/ask_codebase.py,sha256=-4oIe7maa-60w5-QZxOGVt47W0kVd6TB-eIU77fIIMw,3005
36
35
  jarvis/jarvis_tools/ask_user.py,sha256=tIyiKk9F8xchjQ3Yh5fMQjhpflQTuh75HTuXMftGxZY,1827
37
36
  jarvis/jarvis_tools/base.py,sha256=c0DMoDDPxmsqUYJR989zgUs7nIYRY6GWBrAdusIZKjc,656
38
- jarvis/jarvis_tools/chdir.py,sha256=06GAtMqoi5fT1FXD2HUUlHKosVtz-Z8KI13lpEFQw3g,1820
37
+ jarvis/jarvis_tools/chdir.py,sha256=8v7bhYNQOW2BCwS2H4g4a0ozrzKDl-3VXxHE5S7SENY,2981
39
38
  jarvis/jarvis_tools/code_review.py,sha256=ZMWCrkVECCVaT7UyZV0_v49Gecws-x_dQ0NamFUunEs,9775
40
- jarvis/jarvis_tools/create_code_agent.py,sha256=i1crlCWrQrpUuTYueLKgenAy_LhwXhEoFNk8xXo56xc,3899
39
+ jarvis/jarvis_tools/create_code_agent.py,sha256=SAwIGBEz2i-XL-F7J7yXWYqoZzFo1i1sx4yfABh7cAo,3777
41
40
  jarvis/jarvis_tools/create_sub_agent.py,sha256=ldpNn5LczybExvt9Sz4t1ybetLX-dTJIAF5f_oH-Z3M,2869
42
41
  jarvis/jarvis_tools/execute_shell.py,sha256=MwgVyI1O1wshU9yR-DvSWIgoSQpVjtH9JpjVQvSrKF0,2566
42
+ jarvis/jarvis_tools/execute_shell_script.py,sha256=1F4nYnlYyB3JVFylhS-6wABDBD8eyzQ7hm0FP9DlirA,2118
43
43
  jarvis/jarvis_tools/file_operation.py,sha256=9mUGXYrH7dN4XijN1R82SsMXeljOIjIMVzh8hm1aZ1s,5501
44
- jarvis/jarvis_tools/git_commiter.py,sha256=iskOXIhfwvy7XfAQQpjDzy90Tl_dnlZ54hIepiuLBbE,4544
44
+ jarvis/jarvis_tools/git_commiter.py,sha256=LZ4rtyugvkt77HbW4_gSlAxMbMNJ3F-IJsx25k49uJo,5010
45
45
  jarvis/jarvis_tools/lsp_find_definition.py,sha256=xV8YeN1RJfwd2F3gE6OnDeTwl-AnCmrxueHocbXkQOc,4800
46
46
  jarvis/jarvis_tools/lsp_find_references.py,sha256=FohlJeLfTxcMUASfbjOT93hQGtI2WeyTpMGwRwShW_I,4043
47
47
  jarvis/jarvis_tools/lsp_get_diagnostics.py,sha256=bEvbDk8TnKg9TTFFxMrYOJm5TBDgz5gO04WJFQUwQQE,4490
@@ -53,13 +53,13 @@ jarvis/jarvis_tools/rag.py,sha256=eY3GrzagaJIPQ8DmrqNUFFJnIF_GfUxqRjeSengEDss,49
53
53
  jarvis/jarvis_tools/read_code.py,sha256=e0iX9rpwUlSfrhnzgQPNvfqJ-ebxuJybrNaTgb3Hnyw,7290
54
54
  jarvis/jarvis_tools/read_webpage.py,sha256=7QamwBi5s7lD-jTcjD0wsBvkmWPRC9-K-0JkGgeTpvs,3063
55
55
  jarvis/jarvis_tools/registry.py,sha256=XefDvujSfqKX2uLA6tnoJFg5kyBNW0iNAAJZocfIz9w,14836
56
- jarvis/jarvis_tools/search.py,sha256=NHrFpAqg6dtws_9wLJvIZimjeJ-kekETi0Bg0AWMG08,11437
56
+ jarvis/jarvis_tools/search.py,sha256=UqXIUlpqy-0h3CrK23EfdqoXK0SjcthmA90OUGlIYYo,11399
57
57
  jarvis/jarvis_tools/select_code_files.py,sha256=vbEdneWWtAN90OFASohtllTgZW400ZxQbrkgroPK1qc,1902
58
58
  jarvis/jarvis_tools/tool_generator.py,sha256=jdniHyKcEyF9KyouudrCoZBH3czZmQXc3ns0_trZ3yU,6332
59
- jarvis/jarvis_utils/__init__.py,sha256=LYZktMl-iCtGLUa8RXpmDzQVMT_0Pm00aXyFGMBu95o,32207
60
- jarvis_ai_assistant-0.1.123.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
61
- jarvis_ai_assistant-0.1.123.dist-info/METADATA,sha256=f9JvSSga3TWB7iIVfNpCitzs9R-h0CpHHuXrjKH88ZY,13746
62
- jarvis_ai_assistant-0.1.123.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
63
- jarvis_ai_assistant-0.1.123.dist-info/entry_points.txt,sha256=H9Y_q7BZGDsgJijaXHD9GbscllATyKYfg22otrpKEoE,619
64
- jarvis_ai_assistant-0.1.123.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
65
- jarvis_ai_assistant-0.1.123.dist-info/RECORD,,
59
+ jarvis/jarvis_utils/__init__.py,sha256=K74tsMDZPieWX6HxOu1WHX9mJuQah4nOeuzq_j91ecg,33744
60
+ jarvis_ai_assistant-0.1.124.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
61
+ jarvis_ai_assistant-0.1.124.dist-info/METADATA,sha256=iDizLFV77rJQTHB56lhDR9Q5RePLuaeWs7zDGcI1pzA,13749
62
+ jarvis_ai_assistant-0.1.124.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
63
+ jarvis_ai_assistant-0.1.124.dist-info/entry_points.txt,sha256=H9Y_q7BZGDsgJijaXHD9GbscllATyKYfg22otrpKEoE,619
64
+ jarvis_ai_assistant-0.1.124.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
65
+ jarvis_ai_assistant-0.1.124.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,117 +0,0 @@
1
- import os
2
- import re
3
- from typing import Dict, List, Optional, Tuple
4
-
5
- from jarvis.jarvis_code_agent.file_select import select_files
6
- from jarvis.jarvis_codebase.main import CodeBase
7
- from jarvis.jarvis_platform.registry import PlatformRegistry
8
- from jarvis.jarvis_utils import OutputType, PrettyOutput
9
-
10
- def make_question(requirement: str) -> Optional[str]:
11
- """Generate structured questions to gather necessary information for the requirement."""
12
- prompt = f"""
13
- # 🔍 Role Definition
14
- You are a code analysis expert who helps developers understand existing system implementations by asking targeted questions.
15
-
16
- # 🎯 Core Objectives
17
- - Understand current system implementations
18
- - Identify integration points and interfaces
19
- - Discover extension mechanisms
20
- - Map component interactions
21
-
22
- # 📋 Question Categories
23
- ## 1. System Architecture
24
- Focus on system structure and design:
25
- - Existing interfaces and classes
26
- - Component integration patterns
27
- - Extension points and hooks
28
- - System boundaries
29
-
30
- ## 2. Implementation Details
31
- Explore current codebase:
32
- - Workflow implementations
33
- - Hook and callback systems
34
- - Interface hierarchies
35
- - Extension mechanisms
36
-
37
- ## 3. Integration Patterns
38
- Understand connection points:
39
- - Component interactions
40
- - Integration interfaces
41
- - Extension methods
42
- - Plugin systems
43
-
44
- ## 4. Extension Mechanisms
45
- Identify customization options:
46
- - Extension patterns
47
- - Plugin architectures
48
- - Configuration systems
49
- - Customization points
50
-
51
- # 📝 Question Guidelines
52
- ## Good Questions
53
- - "What interfaces currently handle user authentication?"
54
- - "How does the system expose extension points for plugins?"
55
- - "Where are the existing hooks for custom providers?"
56
-
57
- ## Bad Questions
58
- - "How should we implement the new feature?"
59
- - "What's the best way to add this?"
60
- - "Should we create a new class?"
61
-
62
- # 🎨 Question Template
63
- 3-5 specific questions about existing implementations:
64
- <QUESTION>
65
- 1. System architecture question
66
- 2. Implementation details question
67
- 3. Integration or extension question
68
- </QUESTION>
69
-
70
- # 🔎 Investigation Focus
71
- 1. Current System
72
- - Existing implementations
73
- - Active interfaces
74
- - Current patterns
75
-
76
- 2. Integration Points
77
- - Connection methods
78
- - Extension hooks
79
- - Plugin systems
80
-
81
- 3. Extension Options
82
- - Customization points
83
- - Configuration options
84
- - Extension patterns
85
-
86
- # ❗ Important Rules
87
- 1. Focus on EXISTING code
88
- 2. Ask about current patterns
89
- 3. Explore extension points
90
- 4. Investigate interfaces
91
- 5. Map dependencies
92
-
93
- User Requirement:
94
- {requirement}
95
- """
96
- model = PlatformRegistry().get_thinking_platform()
97
- response = model.chat_until_success(prompt)
98
- response = re.search(r'<QUESTION>(.*?)</QUESTION>', response, re.DOTALL)
99
- if response is None:
100
- return None
101
- return response.group(1)
102
-
103
-
104
- def find_relevant_information(user_input: str, root_dir: str) -> Tuple[List[Dict[str, str]], str]:
105
- try:
106
- PrettyOutput.print("从代码库中查找文件...", OutputType.INFO)
107
- codebase = CodeBase(root_dir)
108
- question = make_question(user_input)
109
- if question is None:
110
- return [], ""
111
- files_from_codebase, infomation = codebase.ask_codebase(question)
112
-
113
- selected_files = select_files(files_from_codebase, os.getcwd())
114
- return selected_files, infomation
115
- except Exception:
116
- PrettyOutput.print("查找相关文件失败", OutputType.ERROR)
117
- return [], ""