jarvis-ai-assistant 0.1.121__tar.gz → 0.1.123__tar.gz
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_ai_assistant-0.1.121/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.123}/PKG-INFO +6 -5
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/pyproject.toml +14 -13
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/setup.py +2 -2
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/__init__.py +1 -1
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_code_agent/code_agent.py +62 -59
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_code_agent/file_select.py +7 -6
- jarvis_ai_assistant-0.1.123/src/jarvis/jarvis_code_agent/patch.py +323 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_code_agent/relevant_files.py +2 -4
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_dev/main.py +106 -60
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/base.py +20 -25
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/kimi.py +0 -2
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/openai.py +1 -1
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/file_operation.py +0 -3
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/read_code.py +2 -3
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_utils/__init__.py +15 -8
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123/src/jarvis_ai_assistant.egg-info}/PKG-INFO +6 -5
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis_ai_assistant.egg-info/requires.txt +1 -1
- jarvis_ai_assistant-0.1.121/src/jarvis/jarvis_code_agent/patch.py +0 -446
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/README.md +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_agent/output_handler.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_code_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_codebase/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_codebase/main.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/base.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/cpp.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/go.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/python.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/registry.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_lsp/rust.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_multi_agent/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/ai8.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/ollama.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/oyi.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform/registry.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform_manager/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform_manager/main.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_platform_manager/openai_test.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_rag/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_rag/main.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_smart_shell/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_smart_shell/main.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/ask_codebase.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/ask_user.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/base.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/chdir.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/code_review.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/create_code_agent.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/create_sub_agent.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/execute_shell.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/git_commiter.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_find_definition.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_find_references.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_get_diagnostics.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_prepare_rename.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/lsp_validate_edit.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/methodology.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/rag.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/read_webpage.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/registry.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/search.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/select_code_files.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis/jarvis_tools/tool_generator.py +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.121 → jarvis_ai_assistant-0.1.123}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.123
|
|
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
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: skyfire <skyfireitdiy@hotmail.com>
|
|
8
8
|
License: MIT License
|
|
9
9
|
|
|
10
10
|
Copyright (c) 2025 skyfire
|
|
@@ -28,14 +28,15 @@ License: MIT License
|
|
|
28
28
|
SOFTWARE.
|
|
29
29
|
Project-URL: Homepage, https://github.com/skyfireitdiy/Jarvis
|
|
30
30
|
Keywords: jarvis,ai,assistant,tools,automation
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
31
33
|
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
-
Classifier:
|
|
34
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
33
35
|
Classifier: Programming Language :: Python :: 3
|
|
34
36
|
Classifier: Programming Language :: Python :: 3.8
|
|
35
37
|
Classifier: Programming Language :: Python :: 3.9
|
|
36
38
|
Classifier: Programming Language :: Python :: 3.10
|
|
37
39
|
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
39
40
|
Requires-Python: >=3.8
|
|
40
41
|
Description-Content-Type: text/markdown
|
|
41
42
|
License-File: LICENSE
|
|
@@ -45,7 +46,7 @@ Requires-Dist: colorama>=0.4.6
|
|
|
45
46
|
Requires-Dist: prompt_toolkit>=3.0.0
|
|
46
47
|
Requires-Dist: openai>=1.20.0
|
|
47
48
|
Requires-Dist: playwright>=1.41.1
|
|
48
|
-
Requires-Dist: numpy
|
|
49
|
+
Requires-Dist: numpy==1.17.4
|
|
49
50
|
Requires-Dist: faiss-cpu>=1.8.0
|
|
50
51
|
Requires-Dist: sentence-transformers>=2.2.2
|
|
51
52
|
Requires-Dist: bs4>=0.0.1
|
|
@@ -4,21 +4,22 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jarvis-ai-assistant"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.123"
|
|
8
8
|
description = "Jarvis: An AI assistant that uses tools to interact with the system"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
authors = [{ name = "
|
|
10
|
+
authors = [{ name = "skyfire", email = "skyfireitdiy@hotmail.com" }]
|
|
11
11
|
license = { file = "LICENSE" }
|
|
12
|
-
classifiers = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.8",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
]
|
|
22
23
|
keywords = ["jarvis", "ai", "assistant", "tools", "automation"]
|
|
23
24
|
dependencies = [
|
|
24
25
|
"requests>=2.25.1",
|
|
@@ -27,7 +28,7 @@ dependencies = [
|
|
|
27
28
|
"prompt_toolkit>=3.0.0",
|
|
28
29
|
"openai>=1.20.0",
|
|
29
30
|
"playwright>=1.41.1",
|
|
30
|
-
"numpy
|
|
31
|
+
"numpy==1.17.4",
|
|
31
32
|
"faiss-cpu>=1.8.0",
|
|
32
33
|
"sentence-transformers>=2.2.2",
|
|
33
34
|
"bs4>=0.0.1",
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="jarvis-ai-assistant",
|
|
5
|
-
version="0.1.
|
|
5
|
+
version="0.1.123",
|
|
6
6
|
author="skyfire",
|
|
7
7
|
author_email="skyfireitdiy@hotmail.com",
|
|
8
8
|
description="An AI assistant that uses various tools to interact with the system",
|
|
@@ -48,8 +48,8 @@ setup(
|
|
|
48
48
|
"jarvis-platform-manager=jarvis.jarvis_platform_manager.main:main",
|
|
49
49
|
"jarvis-git-commit=jarvis.jarvis_tools.git_commiter:main",
|
|
50
50
|
"jarvis-code-review=jarvis.jarvis_tools.code_review:main",
|
|
51
|
-
"jarvis-dev=jarvis.jarvis_dev.main:main",
|
|
52
51
|
"jgc=jarvis.jarvis_tools.git_commiter:main",
|
|
52
|
+
"jarvis-dev=jarvis.jarvis_dev.main:main",
|
|
53
53
|
],
|
|
54
54
|
},
|
|
55
55
|
python_requires=">=3.8",
|
|
@@ -29,66 +29,68 @@ class CodeAgent:
|
|
|
29
29
|
"lsp_prepare_rename",
|
|
30
30
|
"lsp_validate_edit"])
|
|
31
31
|
code_system_prompt = """
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- Produce minimal, focused changes
|
|
52
|
-
- Maintain backward compatibility
|
|
53
|
-
- Follow existing style and patterns exactly
|
|
54
|
-
- Complete implementations (NO TODOs/stubs)
|
|
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
|
|
55
51
|
|
|
56
52
|
3. Quality Assurance
|
|
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
|
-
|
|
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
|
+
|
|
75
|
+
## 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
|
+
|
|
88
|
+
## Best Practices
|
|
89
|
+
- Prefer minimal changes over rewrites
|
|
90
|
+
- Preserve existing interfaces
|
|
91
|
+
- Verify line ranges carefully
|
|
92
|
+
- Test edge cases implicitly
|
|
93
|
+
- Document non-obvious logic
|
|
92
94
|
"""
|
|
93
95
|
self.agent = Agent(system_prompt=code_system_prompt,
|
|
94
96
|
name="CodeAgent",
|
|
@@ -167,7 +169,8 @@ Primary:
|
|
|
167
169
|
str: The formatted prompt
|
|
168
170
|
"""
|
|
169
171
|
|
|
170
|
-
return f"""
|
|
172
|
+
return f"""
|
|
173
|
+
# Code Modification Task
|
|
171
174
|
|
|
172
175
|
## User Requirement
|
|
173
176
|
{user_input}
|
|
@@ -227,9 +227,7 @@ def file_input_handler(user_input: str, agent: Any) -> str:
|
|
|
227
227
|
prompt = user_input
|
|
228
228
|
files = []
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
file_refs = re.findall(r'`([^`]+)`', user_input)
|
|
232
|
-
|
|
230
|
+
file_refs = re.findall(r"'([^']+)'", user_input)
|
|
233
231
|
for ref in file_refs:
|
|
234
232
|
# Handle file:start,end or file:start:end format
|
|
235
233
|
if ':' in ref:
|
|
@@ -244,9 +242,12 @@ def file_input_handler(user_input: str, agent: Any) -> str:
|
|
|
244
242
|
raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
|
|
245
243
|
|
|
246
244
|
# Handle special values and Python-style negative indices
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
250
251
|
# Process start line
|
|
251
252
|
if raw_start == 0: # 0表示整个文件
|
|
252
253
|
start_line = 1
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, Any, List, Tuple
|
|
3
|
+
import os
|
|
4
|
+
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
5
|
+
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
6
|
+
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
7
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, user_confirm
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PatchOutputHandler(OutputHandler):
|
|
11
|
+
def name(self) -> str:
|
|
12
|
+
return "PATCH"
|
|
13
|
+
|
|
14
|
+
def handle(self, response: str) -> Tuple[bool, Any]:
|
|
15
|
+
return False, apply_patch(response)
|
|
16
|
+
|
|
17
|
+
def can_handle(self, response: str) -> bool:
|
|
18
|
+
if _parse_patch(response):
|
|
19
|
+
return True
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
def prompt(self) -> str:
|
|
23
|
+
return """
|
|
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
|
+
|
|
38
|
+
<PATCH>
|
|
39
|
+
File path [Range]
|
|
40
|
+
Code content
|
|
41
|
+
</PATCH>
|
|
42
|
+
|
|
43
|
+
Critical Rules:
|
|
44
|
+
- NEVER include unchanged code in patch content
|
|
45
|
+
- ONLY show lines that are being modified/added
|
|
46
|
+
- Maintain original line breaks around modified sections
|
|
47
|
+
- 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>
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
120
|
+
"""解析补丁格式"""
|
|
121
|
+
result = {}
|
|
122
|
+
header_pattern = re.compile(
|
|
123
|
+
r'^\s*"?(.+?)"?\s*\[(\d+)(?:,(\d+))?\]\s*$' # Match file path and line number
|
|
124
|
+
)
|
|
125
|
+
patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
|
|
126
|
+
|
|
127
|
+
for patch in patches:
|
|
128
|
+
# 分割首行和内容
|
|
129
|
+
parts = patch.split('\n', 1)
|
|
130
|
+
if len(parts) < 1:
|
|
131
|
+
continue
|
|
132
|
+
header_line = parts[0].strip()
|
|
133
|
+
content = parts[1] if len(parts) > 1 else ''
|
|
134
|
+
|
|
135
|
+
# 仅在内容非空时添加换行符
|
|
136
|
+
if content and not content.endswith('\n'):
|
|
137
|
+
content += '\n'
|
|
138
|
+
|
|
139
|
+
# 解析文件路径和行号
|
|
140
|
+
header_match = header_pattern.match(header_line)
|
|
141
|
+
if not header_match:
|
|
142
|
+
continue
|
|
143
|
+
|
|
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
|
|
147
|
+
|
|
148
|
+
# 存储参数
|
|
149
|
+
if filepath not in result:
|
|
150
|
+
result[filepath] = []
|
|
151
|
+
result[filepath].append({
|
|
152
|
+
'filepath': filepath,
|
|
153
|
+
'start': start,
|
|
154
|
+
'end': end,
|
|
155
|
+
'content': content # 保留原始内容(可能为空)
|
|
156
|
+
})
|
|
157
|
+
for filepath in result.keys():
|
|
158
|
+
result[filepath] = sorted(result[filepath], key=lambda x: x['start'], reverse=True)
|
|
159
|
+
return result
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def apply_patch(output_str: str) -> str:
|
|
163
|
+
"""Apply patches to files"""
|
|
164
|
+
try:
|
|
165
|
+
patches = _parse_patch(output_str)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
|
|
168
|
+
return ""
|
|
169
|
+
|
|
170
|
+
ret = ""
|
|
171
|
+
|
|
172
|
+
for filepath, patch_list in patches.items():
|
|
173
|
+
for patch in patch_list:
|
|
174
|
+
try:
|
|
175
|
+
handle_code_operation(filepath, patch)
|
|
176
|
+
PrettyOutput.print(f"成功处理 操作", OutputType.SUCCESS)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
|
|
179
|
+
|
|
180
|
+
if has_uncommitted_changes():
|
|
181
|
+
diff = get_diff()
|
|
182
|
+
if handle_commit_workflow(diff):
|
|
183
|
+
ret += "Successfully applied the patch\n"
|
|
184
|
+
# Get modified line ranges
|
|
185
|
+
modified_ranges = get_modified_line_ranges()
|
|
186
|
+
modified_code = ReadCodeTool().execute({"files": [{"path": filepath, "start_line": start, "end_line": end} for filepath, (start, end) in modified_ranges.items()]})
|
|
187
|
+
if modified_code["success"]:
|
|
188
|
+
ret += "New code:\n"
|
|
189
|
+
ret += modified_code["stdout"]
|
|
190
|
+
else:
|
|
191
|
+
ret += "User rejected the patch\nThis is your patch preview:\n"
|
|
192
|
+
ret += diff
|
|
193
|
+
user_input = get_multiline_input("你可以继续输入(输入空行重试,Ctrl+C退出): ")
|
|
194
|
+
if user_input:
|
|
195
|
+
ret += "\n" + user_input
|
|
196
|
+
else:
|
|
197
|
+
ret = ""
|
|
198
|
+
|
|
199
|
+
return ret # Ensure a string is always returned
|
|
200
|
+
|
|
201
|
+
def get_diff() -> str:
|
|
202
|
+
"""使用更安全的subprocess代替os.system"""
|
|
203
|
+
import subprocess
|
|
204
|
+
try:
|
|
205
|
+
subprocess.run(['git', 'add', '.'], check=True)
|
|
206
|
+
result = subprocess.run(
|
|
207
|
+
['git', 'diff', 'HEAD'],
|
|
208
|
+
capture_output=True,
|
|
209
|
+
text=True,
|
|
210
|
+
check=True
|
|
211
|
+
)
|
|
212
|
+
return result.stdout
|
|
213
|
+
finally:
|
|
214
|
+
subprocess.run(['git', 'reset', 'HEAD'], check=True)
|
|
215
|
+
|
|
216
|
+
def handle_commit_workflow(diff:str)->bool:
|
|
217
|
+
"""Handle the git commit workflow and return the commit details.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
221
|
+
"""
|
|
222
|
+
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)
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
git_commiter = GitCommitTool()
|
|
230
|
+
commit_result = git_commiter.execute({})
|
|
231
|
+
return commit_result["success"]
|
|
232
|
+
|
|
233
|
+
def get_modified_line_ranges() -> Dict[str, Tuple[int, int]]:
|
|
234
|
+
"""Get modified line ranges from git diff for all changed files.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Dictionary mapping file paths to tuple with (start_line, end_line) ranges
|
|
238
|
+
for modified sections. Line numbers are 1-based.
|
|
239
|
+
"""
|
|
240
|
+
# Get git diff for all files
|
|
241
|
+
diff_output = os.popen("git show").read()
|
|
242
|
+
|
|
243
|
+
# Parse the diff to get modified files and their line ranges
|
|
244
|
+
result = {}
|
|
245
|
+
current_file = None
|
|
246
|
+
|
|
247
|
+
for line in diff_output.splitlines():
|
|
248
|
+
# Match lines like "+++ b/path/to/file"
|
|
249
|
+
file_match = re.match(r"^\+\+\+ b/(.*)", line)
|
|
250
|
+
if file_match:
|
|
251
|
+
current_file = file_match.group(1)
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
# Match lines like "@@ -100,5 +100,7 @@" where the + part shows new lines
|
|
255
|
+
range_match = re.match(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@", line)
|
|
256
|
+
if range_match and current_file:
|
|
257
|
+
start_line = int(range_match.group(1)) # Keep as 1-based
|
|
258
|
+
line_count = int(range_match.group(2)) if range_match.group(2) else 1
|
|
259
|
+
end_line = start_line + line_count - 1
|
|
260
|
+
result[current_file] = (start_line, end_line)
|
|
261
|
+
|
|
262
|
+
return result
|
|
263
|
+
# New handler functions below ▼▼▼
|
|
264
|
+
|
|
265
|
+
def handle_new_file(filepath: str, patch: Dict[str, Any]):
|
|
266
|
+
"""统一参数格式处理新文件"""
|
|
267
|
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
268
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
269
|
+
f.write(patch['content'])
|
|
270
|
+
|
|
271
|
+
def handle_code_operation(filepath: str, patch: Dict[str, Any]):
|
|
272
|
+
"""处理紧凑格式补丁"""
|
|
273
|
+
try:
|
|
274
|
+
# 新建文件时强制覆盖
|
|
275
|
+
os.makedirs(os.path.dirname(filepath) or '.', exist_ok=True)
|
|
276
|
+
if not os.path.exists(filepath):
|
|
277
|
+
open(filepath, 'w', encoding='utf-8').close()
|
|
278
|
+
with open(filepath, 'r+', encoding='utf-8') as f:
|
|
279
|
+
lines = f.readlines()
|
|
280
|
+
|
|
281
|
+
new_lines = validate_and_apply_changes(
|
|
282
|
+
lines,
|
|
283
|
+
patch['start'],
|
|
284
|
+
patch['end'],
|
|
285
|
+
patch['content']
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
f.seek(0)
|
|
289
|
+
f.writelines(new_lines)
|
|
290
|
+
f.truncate()
|
|
291
|
+
|
|
292
|
+
PrettyOutput.print(f"成功更新 {filepath}", OutputType.SUCCESS)
|
|
293
|
+
|
|
294
|
+
except Exception as e:
|
|
295
|
+
PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
|
|
296
|
+
|
|
297
|
+
def validate_and_apply_changes(
|
|
298
|
+
lines: List[str],
|
|
299
|
+
start: int,
|
|
300
|
+
end: int,
|
|
301
|
+
content: str
|
|
302
|
+
) -> List[str]:
|
|
303
|
+
|
|
304
|
+
new_content = content.splitlines(keepends=True)
|
|
305
|
+
|
|
306
|
+
# 插入操作处理
|
|
307
|
+
if start == end:
|
|
308
|
+
if start < 1 or start > len(lines)+1:
|
|
309
|
+
raise ValueError(f"无效插入位置: {start}")
|
|
310
|
+
# 在指定位置前插入
|
|
311
|
+
return lines[:start-1] + new_content + lines[start-1:]
|
|
312
|
+
|
|
313
|
+
# 范围替换/删除操作
|
|
314
|
+
if start > end:
|
|
315
|
+
raise ValueError(f"起始行{start}不能大于结束行{end}")
|
|
316
|
+
|
|
317
|
+
max_line = len(lines)
|
|
318
|
+
# 自动修正行号范围
|
|
319
|
+
start = max(1, min(start, max_line+1))
|
|
320
|
+
end = max(start, min(end, max_line+1))
|
|
321
|
+
|
|
322
|
+
# 执行替换
|
|
323
|
+
return lines[:start-1] + new_content + lines[end-1:]
|
|
@@ -60,14 +60,12 @@ Identify customization options:
|
|
|
60
60
|
- "Should we create a new class?"
|
|
61
61
|
|
|
62
62
|
# 🎨 Question Template
|
|
63
|
-
|
|
63
|
+
3-5 specific questions about existing implementations:
|
|
64
64
|
<QUESTION>
|
|
65
|
-
[3-5 specific questions about existing implementations:
|
|
66
65
|
1. System architecture question
|
|
67
66
|
2. Implementation details question
|
|
68
|
-
3. Integration or extension question
|
|
67
|
+
3. Integration or extension question
|
|
69
68
|
</QUESTION>
|
|
70
|
-
```
|
|
71
69
|
|
|
72
70
|
# 🔎 Investigation Focus
|
|
73
71
|
1. Current System
|