jarvis-ai-assistant 0.1.123__py3-none-any.whl → 0.1.125__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 +1 -1
- jarvis/jarvis_agent/__init__.py +19 -21
- jarvis/jarvis_code_agent/code_agent.py +205 -119
- jarvis/jarvis_code_agent/file_select.py +6 -105
- jarvis/jarvis_code_agent/patch.py +192 -259
- jarvis/jarvis_codebase/main.py +6 -2
- jarvis/jarvis_dev/main.py +6 -4
- jarvis/jarvis_git_squash/__init__.py +0 -0
- jarvis/jarvis_git_squash/main.py +81 -0
- jarvis/jarvis_lsp/cpp.py +1 -1
- jarvis/jarvis_lsp/go.py +1 -1
- jarvis/jarvis_lsp/registry.py +2 -2
- jarvis/jarvis_lsp/rust.py +1 -1
- jarvis/jarvis_multi_agent/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +2 -1
- jarvis/jarvis_platform/base.py +20 -25
- jarvis/jarvis_platform/kimi.py +2 -3
- jarvis/jarvis_platform/ollama.py +3 -1
- jarvis/jarvis_platform/openai.py +1 -1
- jarvis/jarvis_platform/oyi.py +2 -1
- jarvis/jarvis_platform/registry.py +2 -1
- jarvis/jarvis_platform_manager/main.py +4 -6
- jarvis/jarvis_platform_manager/openai_test.py +0 -1
- jarvis/jarvis_rag/main.py +5 -2
- jarvis/jarvis_smart_shell/main.py +9 -4
- jarvis/jarvis_tools/ask_codebase.py +12 -7
- jarvis/jarvis_tools/ask_user.py +3 -2
- jarvis/jarvis_tools/base.py +21 -7
- jarvis/jarvis_tools/chdir.py +25 -1
- jarvis/jarvis_tools/code_review.py +13 -14
- jarvis/jarvis_tools/create_code_agent.py +4 -7
- jarvis/jarvis_tools/create_sub_agent.py +2 -2
- jarvis/jarvis_tools/execute_shell.py +3 -1
- jarvis/jarvis_tools/execute_shell_script.py +58 -0
- jarvis/jarvis_tools/file_operation.py +3 -2
- jarvis/jarvis_tools/git_commiter.py +26 -17
- jarvis/jarvis_tools/lsp_find_definition.py +1 -1
- jarvis/jarvis_tools/lsp_find_references.py +1 -1
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -11
- jarvis/jarvis_tools/lsp_get_document_symbols.py +1 -1
- jarvis/jarvis_tools/lsp_prepare_rename.py +1 -1
- jarvis/jarvis_tools/lsp_validate_edit.py +1 -1
- jarvis/jarvis_tools/methodology.py +4 -1
- jarvis/jarvis_tools/rag.py +22 -15
- jarvis/jarvis_tools/read_code.py +4 -3
- jarvis/jarvis_tools/read_webpage.py +2 -1
- jarvis/jarvis_tools/registry.py +4 -1
- jarvis/jarvis_tools/{search.py → search_web.py} +5 -3
- jarvis/jarvis_tools/select_code_files.py +1 -1
- jarvis/jarvis_utils/__init__.py +19 -941
- jarvis/jarvis_utils/config.py +138 -0
- jarvis/jarvis_utils/embedding.py +201 -0
- jarvis/jarvis_utils/git_utils.py +120 -0
- jarvis/jarvis_utils/globals.py +82 -0
- jarvis/jarvis_utils/input.py +161 -0
- jarvis/jarvis_utils/methodology.py +128 -0
- jarvis/jarvis_utils/output.py +235 -0
- jarvis/jarvis_utils/utils.py +150 -0
- jarvis_ai_assistant-0.1.125.dist-info/METADATA +291 -0
- jarvis_ai_assistant-0.1.125.dist-info/RECORD +75 -0
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/WHEEL +1 -1
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/entry_points.txt +1 -0
- jarvis/jarvis_code_agent/relevant_files.py +0 -117
- jarvis_ai_assistant-0.1.123.dist-info/METADATA +0 -461
- jarvis_ai_assistant-0.1.123.dist-info/RECORD +0 -65
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.125.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import
|
|
3
|
-
import time
|
|
4
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
2
|
+
from typing import Any, Callable, List, Optional, Tuple, Union
|
|
5
3
|
|
|
6
4
|
from prompt_toolkit import prompt
|
|
7
5
|
import yaml
|
|
@@ -10,7 +8,15 @@ from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
|
10
8
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
11
9
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
12
10
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
13
|
-
from jarvis.jarvis_utils import PrettyOutput, OutputType
|
|
11
|
+
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
|
12
|
+
from jarvis.jarvis_utils.embedding import get_context_token_count
|
|
13
|
+
from jarvis.jarvis_utils.config import is_auto_complete, is_execute_tool_confirm, is_need_summary, is_record_methodology, is_use_methodology
|
|
14
|
+
from jarvis.jarvis_utils.methodology import load_methodology
|
|
15
|
+
from jarvis.jarvis_utils.globals import make_agent_name, set_agent, delete_agent
|
|
16
|
+
from jarvis.jarvis_utils.input import get_multiline_input
|
|
17
|
+
from jarvis.jarvis_utils.config import get_max_token_count
|
|
18
|
+
from jarvis.jarvis_utils.utils import init_env
|
|
19
|
+
from jarvis.jarvis_utils.utils import user_confirm
|
|
14
20
|
import os
|
|
15
21
|
|
|
16
22
|
class Agent:
|
|
@@ -37,7 +43,7 @@ class Agent:
|
|
|
37
43
|
summary_prompt: Optional[str] = None,
|
|
38
44
|
auto_complete: Optional[bool] = None,
|
|
39
45
|
output_handler: List[OutputHandler] = [],
|
|
40
|
-
input_handler: Optional[List[Callable]] = None,
|
|
46
|
+
input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]] = None,
|
|
41
47
|
use_methodology: Optional[bool] = None,
|
|
42
48
|
record_methodology: Optional[bool] = None,
|
|
43
49
|
need_summary: Optional[bool] = None,
|
|
@@ -143,7 +149,7 @@ The following actions are at your disposal:
|
|
|
143
149
|
|
|
144
150
|
|
|
145
151
|
|
|
146
|
-
def _call_model(self, message: str) -> str:
|
|
152
|
+
def _call_model(self, message: str) -> str:
|
|
147
153
|
"""Call the AI model with retry logic.
|
|
148
154
|
|
|
149
155
|
Args:
|
|
@@ -155,20 +161,12 @@ The following actions are at your disposal:
|
|
|
155
161
|
Note:
|
|
156
162
|
Will retry with exponential backoff up to 30 seconds between retries
|
|
157
163
|
"""
|
|
158
|
-
sleep_time = 5
|
|
159
164
|
for handler in self.input_handler:
|
|
160
|
-
message = handler(message, self)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else:
|
|
166
|
-
PrettyOutput.print(f"模型调用失败,正在重试... 等待 {sleep_time}s", OutputType.INFO)
|
|
167
|
-
time.sleep(sleep_time)
|
|
168
|
-
sleep_time *= 2
|
|
169
|
-
if sleep_time > 30:
|
|
170
|
-
sleep_time = 30
|
|
171
|
-
continue
|
|
165
|
+
message, need_return = handler(message, self)
|
|
166
|
+
if need_return:
|
|
167
|
+
return message
|
|
168
|
+
return self.model.chat_until_success(message) # type: ignore
|
|
169
|
+
|
|
172
170
|
|
|
173
171
|
|
|
174
172
|
def _summarize_and_clear_history(self) -> None:
|
|
@@ -276,7 +274,7 @@ Please continue the task based on the above information.
|
|
|
276
274
|
"""Process user input and execute the task.
|
|
277
275
|
|
|
278
276
|
Args:
|
|
279
|
-
user_input:
|
|
277
|
+
user_input: My task description or request
|
|
280
278
|
file_list: Optional list of files to process
|
|
281
279
|
|
|
282
280
|
Returns:
|
|
@@ -327,7 +325,7 @@ Please continue the task based on the above information.
|
|
|
327
325
|
return self._complete_task()
|
|
328
326
|
|
|
329
327
|
# 获取用户输入
|
|
330
|
-
user_input = get_multiline_input(f"{self.name}:
|
|
328
|
+
user_input = get_multiline_input(f"{self.name}: 请输入,或输入空行来结束当前任务:")
|
|
331
329
|
|
|
332
330
|
if user_input:
|
|
333
331
|
self.prompt = user_input
|
|
@@ -1,15 +1,95 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import subprocess
|
|
1
3
|
import os
|
|
2
|
-
from typing import
|
|
4
|
+
from typing import Any, Tuple
|
|
3
5
|
|
|
4
6
|
from jarvis.jarvis_agent import Agent
|
|
5
|
-
from jarvis.jarvis_code_agent.
|
|
6
|
-
from jarvis.jarvis_code_agent.patch import PatchOutputHandler
|
|
7
|
-
from jarvis.jarvis_code_agent.relevant_files import find_relevant_information
|
|
7
|
+
from jarvis.jarvis_code_agent.patch import PatchOutputHandler, shell_input_handler
|
|
8
8
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
9
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
10
|
-
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
11
10
|
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
12
|
-
from jarvis.
|
|
11
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
12
|
+
from jarvis.jarvis_utils.git_utils import find_git_root, get_commits_between, get_latest_commit_hash, has_uncommitted_changes
|
|
13
|
+
from jarvis.jarvis_utils.input import get_multiline_input
|
|
14
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
15
|
+
from jarvis.jarvis_utils.utils import init_env, user_confirm
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def file_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
21
|
+
prompt = user_input
|
|
22
|
+
files = []
|
|
23
|
+
|
|
24
|
+
file_refs = re.findall(r"'([^']+)'", user_input)
|
|
25
|
+
for ref in file_refs:
|
|
26
|
+
# Handle file:start,end or file:start:end format
|
|
27
|
+
if ':' in ref:
|
|
28
|
+
file_path, line_range = ref.split(':', 1)
|
|
29
|
+
# Initialize with default values
|
|
30
|
+
start_line = 1 # 1-based
|
|
31
|
+
end_line = -1
|
|
32
|
+
|
|
33
|
+
# Process line range if specified
|
|
34
|
+
if ',' in line_range or ':' in line_range:
|
|
35
|
+
try:
|
|
36
|
+
raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
|
|
37
|
+
|
|
38
|
+
# Handle special values and Python-style negative indices
|
|
39
|
+
try:
|
|
40
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
41
|
+
total_lines = len(f.readlines())
|
|
42
|
+
except FileNotFoundError:
|
|
43
|
+
PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
|
|
44
|
+
continue
|
|
45
|
+
# Process start line
|
|
46
|
+
if raw_start == 0: # 0表示整个文件
|
|
47
|
+
start_line = 1
|
|
48
|
+
end_line = total_lines
|
|
49
|
+
else:
|
|
50
|
+
start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
|
|
51
|
+
|
|
52
|
+
# Process end line
|
|
53
|
+
if raw_end == 0: # 0表示整个文件(如果start也是0)
|
|
54
|
+
end_line = total_lines
|
|
55
|
+
else:
|
|
56
|
+
end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
|
|
57
|
+
|
|
58
|
+
# Auto-correct ranges
|
|
59
|
+
start_line = max(1, min(start_line, total_lines))
|
|
60
|
+
end_line = max(start_line, min(end_line, total_lines))
|
|
61
|
+
|
|
62
|
+
# Final validation
|
|
63
|
+
if start_line < 1 or end_line > total_lines or start_line > end_line:
|
|
64
|
+
raise ValueError
|
|
65
|
+
|
|
66
|
+
except:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
# Add file if it exists
|
|
70
|
+
if os.path.isfile(file_path):
|
|
71
|
+
files.append({
|
|
72
|
+
"path": file_path,
|
|
73
|
+
"start_line": start_line,
|
|
74
|
+
"end_line": end_line
|
|
75
|
+
})
|
|
76
|
+
else:
|
|
77
|
+
# Handle simple file path
|
|
78
|
+
if os.path.isfile(ref):
|
|
79
|
+
files.append({
|
|
80
|
+
"path": ref,
|
|
81
|
+
"start_line": 1, # 1-based
|
|
82
|
+
"end_line": -1
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
# Read and process files if any were found
|
|
86
|
+
if files:
|
|
87
|
+
result = ReadCodeTool().execute({"files": files})
|
|
88
|
+
if result["success"]:
|
|
89
|
+
return result["stdout"] + "\n" + prompt, False
|
|
90
|
+
|
|
91
|
+
return prompt, False
|
|
92
|
+
|
|
13
93
|
|
|
14
94
|
|
|
15
95
|
class CodeAgent:
|
|
@@ -18,10 +98,11 @@ class CodeAgent:
|
|
|
18
98
|
tool_registry = ToolRegistry()
|
|
19
99
|
tool_registry.use_tools(["read_code",
|
|
20
100
|
"execute_shell",
|
|
21
|
-
"
|
|
101
|
+
"execute_shell_script",
|
|
102
|
+
"search_web",
|
|
22
103
|
"create_code_agent",
|
|
23
|
-
"ask_user",
|
|
24
|
-
"ask_codebase",
|
|
104
|
+
"ask_user",
|
|
105
|
+
"ask_codebase",
|
|
25
106
|
"lsp_get_document_symbols",
|
|
26
107
|
"lsp_get_diagnostics",
|
|
27
108
|
"lsp_find_references",
|
|
@@ -30,67 +111,95 @@ class CodeAgent:
|
|
|
30
111
|
"lsp_validate_edit"])
|
|
31
112
|
code_system_prompt = """
|
|
32
113
|
# Role: Senior Code Engineer
|
|
33
|
-
Expert in precise code modifications with
|
|
34
|
-
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
|
|
114
|
+
Expert in safe, precise code modifications with rigorous validation processes.
|
|
115
|
+
|
|
116
|
+
## Core Principles
|
|
117
|
+
1. Safety First: Never break existing functionality
|
|
118
|
+
2. Precision Engineering: Minimal, targeted changes
|
|
119
|
+
3. Full Traceability: Document all decisions
|
|
120
|
+
4. Validation-Driven: Verify at every stage
|
|
121
|
+
|
|
122
|
+
## Tool Usage Protocol
|
|
123
|
+
1. Analysis Tools:
|
|
124
|
+
- read_code: Inspect code segments before modification
|
|
125
|
+
- lsp_get_document_symbols: Map code structure
|
|
126
|
+
- lsp_find_references: Understand usage patterns
|
|
127
|
+
- lsp_find_definition: Trace implementation details
|
|
128
|
+
|
|
129
|
+
2. Validation Tools:
|
|
130
|
+
- lsp_prepare_rename: Safe refactoring check
|
|
131
|
+
- lsp_validate_edit: Pre-commit validation
|
|
132
|
+
- lsp_get_diagnostics: Post-modification checks
|
|
133
|
+
|
|
134
|
+
3. System Tools:
|
|
135
|
+
- execute_shell: For git operations and grep searches
|
|
136
|
+
- ask_codebase: Query code knowledge base
|
|
137
|
+
- search_web: Technical reference lookup
|
|
138
|
+
|
|
139
|
+
## Workflow (PDCA Cycle)
|
|
140
|
+
1. Plan:
|
|
141
|
+
- Analyze requirements with ask_user
|
|
142
|
+
- Map existing code using LSP tools
|
|
143
|
+
- Identify impact areas with find_references
|
|
144
|
+
- Create rollback plan using git
|
|
145
|
+
|
|
146
|
+
2. Do:
|
|
147
|
+
- Make atomic changes in protected blocks
|
|
148
|
+
- Immediately invoke lsp_validate_edit to validate changes
|
|
149
|
+
- Automatically run lsp_get_diagnostics after each change
|
|
150
|
+
- If errors found, use lsp_find_references and lsp_find_definition for immediate remediation
|
|
151
|
+
- Validate syntax with LSP after each change
|
|
152
|
+
|
|
153
|
+
3. Check:
|
|
154
|
+
- Mandatory lsp_get_diagnostics for full diagnostic report
|
|
155
|
+
- Validate all renames with lsp_prepare_rename
|
|
156
|
+
- Execute lsp_validate_edit on all modified files
|
|
157
|
+
- If errors detected, enter remediation loop until all checks pass
|
|
158
|
+
|
|
159
|
+
4. Act:
|
|
160
|
+
- Commit with detailed message using git
|
|
161
|
+
- Prepare rollback script if needed
|
|
162
|
+
- Conduct post-implementation review
|
|
163
|
+
|
|
164
|
+
## Code Modification Standards
|
|
165
|
+
1. Pre-Change Requirements:
|
|
166
|
+
- Complete code analysis report
|
|
167
|
+
- Impact assessment matrix
|
|
168
|
+
- Rollback procedure document
|
|
169
|
+
|
|
170
|
+
2. Change Implementation:
|
|
171
|
+
- Single-responsibility changes
|
|
172
|
+
- Strict line range validation (±3 line buffer)
|
|
173
|
+
- Interface compatibility checks
|
|
174
|
+
|
|
175
|
+
3. Validation Checklist:
|
|
176
|
+
[ ] Execute lsp_get_diagnostics and ensure zero errors
|
|
177
|
+
[ ] All changes validated with lsp_validate_edit
|
|
178
|
+
[ ] Confirm impact scope with lsp_find_references
|
|
179
|
+
[ ] Verify rename safety with lsp_prepare_rename
|
|
180
|
+
|
|
181
|
+
4. Post-Change:
|
|
182
|
+
- Code review simulation
|
|
183
|
+
- Version control audit
|
|
184
|
+
- Change log update
|
|
185
|
+
|
|
186
|
+
## Critical Requirements
|
|
187
|
+
1. Mandatory Analysis:
|
|
188
|
+
- Full symbol tracing before modification
|
|
189
|
+
- Cross-file impact analysis
|
|
190
|
+
- Dependency mapping
|
|
191
|
+
|
|
192
|
+
2. Prohibited Actions:
|
|
193
|
+
- Proceed without passing lsp_get_diagnostics checks
|
|
194
|
+
- Submit changes without lsp_validate_edit validation
|
|
195
|
+
- Multi-feature combined changes
|
|
196
|
+
- Untested interface alterations
|
|
197
|
+
|
|
198
|
+
3. Emergency Protocols:
|
|
199
|
+
- Immediately halt and rollback on lsp_get_diagnostics errors
|
|
200
|
+
- Prioritize fixing validation errors if lsp_validate_edit fails
|
|
201
|
+
- User notification on unexpected behavior
|
|
202
|
+
- Post-mortem analysis for any regression
|
|
94
203
|
"""
|
|
95
204
|
self.agent = Agent(system_prompt=code_system_prompt,
|
|
96
205
|
name="CodeAgent",
|
|
@@ -100,7 +209,7 @@ Make it count.
|
|
|
100
209
|
output_handler=[tool_registry, PatchOutputHandler()],
|
|
101
210
|
platform=PlatformRegistry().get_codegen_platform(),
|
|
102
211
|
record_methodology=False,
|
|
103
|
-
input_handler=[file_input_handler],
|
|
212
|
+
input_handler=[shell_input_handler, file_input_handler],
|
|
104
213
|
need_summary=False)
|
|
105
214
|
|
|
106
215
|
|
|
@@ -114,26 +223,6 @@ Make it count.
|
|
|
114
223
|
git_commiter.execute({})
|
|
115
224
|
|
|
116
225
|
|
|
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
226
|
|
|
138
227
|
def run(self, user_input: str) :
|
|
139
228
|
"""Run the code agent with the given user input.
|
|
@@ -146,41 +235,38 @@ Make it count.
|
|
|
146
235
|
"""
|
|
147
236
|
try:
|
|
148
237
|
self._init_env()
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
238
|
+
start_commit = get_latest_commit_hash()
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
self.agent.run(user_input)
|
|
242
|
+
|
|
243
|
+
end_commit = get_latest_commit_hash()
|
|
244
|
+
# Print commit history between start and end commits
|
|
245
|
+
if start_commit and end_commit:
|
|
246
|
+
commits = get_commits_between(start_commit, end_commit)
|
|
152
247
|
else:
|
|
153
|
-
|
|
154
|
-
self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files), information))
|
|
248
|
+
commits = []
|
|
155
249
|
|
|
250
|
+
if commits:
|
|
251
|
+
commit_messages = "检测到以下提交记录:\n" + "\n".join([f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits])
|
|
252
|
+
PrettyOutput.print(commit_messages, OutputType.INFO)
|
|
253
|
+
|
|
254
|
+
if commits and user_confirm("是否接受以上提交记录?", True):
|
|
255
|
+
if len(commits) > 1 and user_confirm("是否要合并为一个更清晰的提交记录?", True):
|
|
256
|
+
# Reset to start commit
|
|
257
|
+
subprocess.run(["git", "reset", "--soft", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
258
|
+
# Create new commit
|
|
259
|
+
git_commiter = GitCommitTool()
|
|
260
|
+
git_commiter.execute({})
|
|
261
|
+
elif start_commit:
|
|
262
|
+
os.system(f"git reset --hard {start_commit}")
|
|
263
|
+
PrettyOutput.print("已重置到初始提交", OutputType.INFO)
|
|
264
|
+
|
|
156
265
|
except Exception as e:
|
|
157
266
|
return f"Error during execution: {str(e)}"
|
|
158
267
|
|
|
159
268
|
|
|
160
269
|
|
|
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
270
|
def main():
|
|
185
271
|
"""Jarvis main entry point"""
|
|
186
272
|
# Add argument parser
|
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Dict, List
|
|
4
4
|
from prompt_toolkit import PromptSession
|
|
5
5
|
from prompt_toolkit.completion import Completer, Completion
|
|
6
|
-
|
|
7
|
-
from jarvis.jarvis_utils import
|
|
6
|
+
|
|
7
|
+
from jarvis.jarvis_utils.input import get_single_line_input
|
|
8
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
9
|
+
from jarvis.jarvis_utils.utils import user_confirm
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def _parse_file_selection(input_str: str, max_index: int) -> List[int]:
|
|
11
|
-
"""Parse file selection expression
|
|
12
|
-
|
|
13
|
-
Supported formats:
|
|
14
|
-
- Single number: "1"
|
|
15
|
-
- Comma-separated: "1,3,5"
|
|
16
|
-
- Range: "1-5"
|
|
17
|
-
- Combination: "1,3-5,7"
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
input_str: User input selection expression
|
|
21
|
-
max_index: Maximum selectable index
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
List[int]: Selected index list (starting from 0)
|
|
25
|
-
"""
|
|
26
13
|
selected = set()
|
|
27
14
|
|
|
28
15
|
# Remove all whitespace characters
|
|
@@ -205,7 +192,7 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
205
192
|
continue
|
|
206
193
|
|
|
207
194
|
try:
|
|
208
|
-
selected_files.append({"file": path, "reason": "
|
|
195
|
+
selected_files.append({"file": path, "reason": "I Added"})
|
|
209
196
|
tips += f"\n文件已添加: {path}"
|
|
210
197
|
except Exception as e:
|
|
211
198
|
tips += f"\n读取文件失败: {str(e)}"
|
|
@@ -213,89 +200,3 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
213
200
|
if tips:
|
|
214
201
|
PrettyOutput.print(tips, OutputType.INFO)
|
|
215
202
|
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
|