feat: Add aider integration workflow command
This commit is contained in:
parent
c4352d8486
commit
4e38d7785b
19
merico/aider/README.md
Normal file
19
merico/aider/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
### 操作指南
|
||||
|
||||
aider工作流命令使用步骤如下:
|
||||
|
||||
1. 确保已经使用 `/aider.files.add` 命令添加了需要处理的文件。
|
||||
2. 输入 `/aider <message>` 命令,其中 `<message>` 是你想要aider执行的任务描述。
|
||||
3. 等待aider生成建议的更改。
|
||||
4. 系统会自动显示每个文件的Diff View,你可以选择是否接受修改。
|
||||
5. 对于多个文件的更改,系统会在每个文件之后询问是否继续查看下一个文件的更改。
|
||||
|
||||
注意事项:
|
||||
- 如果没有添加任何文件到aider,命令将会提示你先使用 'aider.files.add' 命令添加文件。
|
||||
- 你可以使用 `aider.files.remove` 命令从aider中移除文件。
|
||||
- 所有的更改都会在IDE中以Diff View的形式展示,你可以在查看后决定是否应用这些更改。
|
||||
|
||||
使用示例:
|
||||
/aider 重构这段代码以提高性能
|
||||
|
||||
这个命令会让aider分析当前添加的文件,并提供重构建议以提高代码性能。
|
216
merico/aider/command.py
Normal file
216
merico/aider/command.py
Normal file
@ -0,0 +1,216 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
from devchat.ide import IDEService
|
||||
from lib.chatmark import Button
|
||||
|
||||
GLOBAL_CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".chat", ".workflow_config.json")
|
||||
|
||||
def save_config(config_path, item, value):
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
else:
|
||||
config = {}
|
||||
|
||||
config[item] = value
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=4)
|
||||
|
||||
def write_python_path_to_config():
|
||||
"""
|
||||
Write the current system Python path to the configuration.
|
||||
"""
|
||||
python_path = sys.executable
|
||||
save_config(GLOBAL_CONFIG_PATH, 'aider_python', python_path)
|
||||
print(f"Python path '{python_path}' has been written to the configuration.")
|
||||
|
||||
def get_aider_files():
|
||||
"""
|
||||
从.chat/.aider_files文件中读取aider文件列表
|
||||
"""
|
||||
aider_files_path = os.path.join('.chat', '.aider_files')
|
||||
if not os.path.exists(aider_files_path):
|
||||
return []
|
||||
|
||||
with open(aider_files_path, 'r') as f:
|
||||
return [line.strip() for line in f if line.strip()]
|
||||
|
||||
def run_aider(message, files):
|
||||
"""
|
||||
运行aider命令
|
||||
"""
|
||||
python = sys.executable
|
||||
model = os.environ.get('LLM_MODEL', 'gpt-3.5-turbo-1106')
|
||||
|
||||
cmd = [
|
||||
python,
|
||||
"-m",
|
||||
"aider",
|
||||
"--model",
|
||||
f"openai/{model}",
|
||||
"--yes",
|
||||
"--no-auto-commits",
|
||||
"--dry-run",
|
||||
"--no-pretty",
|
||||
"--message",
|
||||
message,
|
||||
] + files
|
||||
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
|
||||
has_started = False
|
||||
aider_output = ""
|
||||
for line in process.stdout:
|
||||
if "run with --help" in line or 'run "aider --help"' in line:
|
||||
has_started = True
|
||||
continue
|
||||
if has_started:
|
||||
aider_output += line
|
||||
print(line, end="", flush=True)
|
||||
|
||||
return_code = process.wait()
|
||||
|
||||
if return_code != 0:
|
||||
for line in process.stderr:
|
||||
print(f"Error: {line.strip()}", file=sys.stderr)
|
||||
sys.exit(return_code)
|
||||
|
||||
return aider_output
|
||||
|
||||
def apply_changes(changes, files):
|
||||
"""
|
||||
应用aider生成的更改
|
||||
"""
|
||||
changes_file = '.chat/changes.txt'
|
||||
os.makedirs(os.path.dirname(changes_file), exist_ok=True)
|
||||
with open(changes_file, 'w') as f:
|
||||
f.write(changes)
|
||||
|
||||
python = sys.executable
|
||||
model = os.environ.get('LLM_MODEL', 'gpt-3.5-turbo-1106')
|
||||
|
||||
cmd = [
|
||||
python,
|
||||
"-m",
|
||||
"aider",
|
||||
"--model",
|
||||
f"openai/{model}",
|
||||
"--yes",
|
||||
"--no-auto-commits",
|
||||
"--apply",
|
||||
changes_file,
|
||||
] + files
|
||||
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
|
||||
has_started = False
|
||||
for line in process.stdout:
|
||||
if "Model:" in line:
|
||||
has_started = True
|
||||
continue
|
||||
if has_started:
|
||||
print(line, end="", flush=True)
|
||||
|
||||
return_code = process.wait()
|
||||
|
||||
if return_code != 0:
|
||||
for line in process.stderr:
|
||||
print(f"Error: {line.strip()}", file=sys.stderr)
|
||||
sys.exit(return_code)
|
||||
|
||||
os.remove(changes_file)
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to run the aider command.
|
||||
|
||||
This function performs the following tasks:
|
||||
1. Checks for correct command-line usage
|
||||
2. Writes the current Python path to the configuration
|
||||
3. Retrieves the list of files to be processed
|
||||
4. Runs the aider command with the given message
|
||||
5. Applies the suggested changes
|
||||
6. Displays the differences in the IDE
|
||||
|
||||
Usage: python command.py <message>
|
||||
"""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python command.py <message>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
write_python_path_to_config()
|
||||
|
||||
message = sys.argv[1]
|
||||
files = get_aider_files()
|
||||
|
||||
if not files:
|
||||
print("No files added to aider. Please add files using 'aider.files.add' command.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("Running aider...\n", flush=True)
|
||||
changes = run_aider(message, files)
|
||||
|
||||
if not changes:
|
||||
print("No changes suggested by aider.")
|
||||
sys.exit(0)
|
||||
|
||||
print("\nApplying changes...\n", flush=True)
|
||||
|
||||
# 保存原始文件内容
|
||||
original_contents = {}
|
||||
for file in files:
|
||||
with open(file, 'r') as f:
|
||||
original_contents[file] = f.read()
|
||||
|
||||
# 应用更改
|
||||
apply_changes(changes, files)
|
||||
|
||||
# 读取更新后的文件内容
|
||||
updated_contents = {}
|
||||
for file in files:
|
||||
with open(file, 'r') as f:
|
||||
updated_contents[file] = f.read()
|
||||
|
||||
# 还原原始文件内容
|
||||
for file in files:
|
||||
with open(file, 'w') as f:
|
||||
f.write(original_contents[file])
|
||||
|
||||
# 使用 IDEService 展示差异
|
||||
ide_service = IDEService()
|
||||
for index,file in enumerate(files):
|
||||
ide_service.diff_apply(file, updated_contents[file])
|
||||
if index < len(files) - 1:
|
||||
# 等待用户确认
|
||||
button = Button(
|
||||
[
|
||||
"Show Next Changes",
|
||||
"Cancel"
|
||||
],
|
||||
)
|
||||
button.render()
|
||||
|
||||
idx = button.clicked
|
||||
print("click button:", idx)
|
||||
if idx == 0:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
print("Changes have been displayed in the IDE.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
8
merico/aider/command.yml
Normal file
8
merico/aider/command.yml
Normal file
@ -0,0 +1,8 @@
|
||||
description: "aider command"
|
||||
workflow_python:
|
||||
env_name: devchat-aider-env
|
||||
version: 3.11.0
|
||||
dependencies: requirements.txt
|
||||
input: required
|
||||
steps:
|
||||
- run: $workflow_python $command_path/command.py "$input"
|
59
merico/aider/files/add/command.py
Normal file
59
merico/aider/files/add/command.py
Normal file
@ -0,0 +1,59 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
def is_valid_path(path):
|
||||
"""
|
||||
检查路径是否为有效的文件路径形式
|
||||
"""
|
||||
try:
|
||||
# 尝试规范化路径
|
||||
normalized_path = os.path.normpath(path)
|
||||
# 检查路径是否是绝对路径或相对路径
|
||||
return os.path.isabs(normalized_path) or not os.path.dirname(normalized_path) == normalized_path
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def add_file(file_path):
|
||||
# 1. 检查是否为有效的文件路径形式
|
||||
if not is_valid_path(file_path):
|
||||
print(f"Error: '{file_path}' is not a valid file path format.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 获取绝对路径
|
||||
abs_file_path = file_path.strip()
|
||||
|
||||
# 2. 将新增文件路径存储到.chat/.aider_files文件中
|
||||
aider_files_path = os.path.join('.chat', '.aider_files')
|
||||
|
||||
# 确保.chat目录存在
|
||||
os.makedirs(os.path.dirname(aider_files_path), exist_ok=True)
|
||||
|
||||
# 读取现有文件列表
|
||||
existing_files = set()
|
||||
if os.path.exists(aider_files_path):
|
||||
with open(aider_files_path, 'r') as f:
|
||||
existing_files = set(line.strip() for line in f)
|
||||
|
||||
# 添加新文件
|
||||
existing_files.add(abs_file_path)
|
||||
|
||||
# 写入更新后的文件列表
|
||||
with open(aider_files_path, 'w') as f:
|
||||
for file in sorted(existing_files):
|
||||
f.write(f"{file}\n")
|
||||
|
||||
print(f"Added '{abs_file_path}' to aider files.")
|
||||
print("\nCurrent aider files:")
|
||||
for file in sorted(existing_files):
|
||||
print(f"- {file}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2 or sys.argv[1].strip() == "":
|
||||
print("Usage: /aider.files.add <file_path>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
add_file(file_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
4
merico/aider/files/add/command.yml
Normal file
4
merico/aider/files/add/command.yml
Normal file
@ -0,0 +1,4 @@
|
||||
description: "add files to aider"
|
||||
input: required
|
||||
steps:
|
||||
- run: $devchat_python $command_path/command.py "$input"
|
28
merico/aider/files/list/command.py
Normal file
28
merico/aider/files/list/command.py
Normal file
@ -0,0 +1,28 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
def list_files():
|
||||
aider_files_path = os.path.join('.chat', '.aider_files')
|
||||
|
||||
# 确保.chat/.aider_files文件存在
|
||||
if not os.path.exists(aider_files_path):
|
||||
print("No files have been added to aider yet.")
|
||||
sys.exit(0)
|
||||
|
||||
# 读取文件列表
|
||||
with open(aider_files_path, 'r') as f:
|
||||
files = [line.strip() for line in f]
|
||||
|
||||
# 打印文件列表
|
||||
if files:
|
||||
print("Aider files:")
|
||||
for file in sorted(files):
|
||||
print(f"- {file}")
|
||||
else:
|
||||
print("No files found in aider.")
|
||||
|
||||
def main():
|
||||
list_files()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
3
merico/aider/files/list/command.yml
Normal file
3
merico/aider/files/list/command.yml
Normal file
@ -0,0 +1,3 @@
|
||||
description: "list files in aider"
|
||||
steps:
|
||||
- run: $devchat_python $command_path/command.py
|
65
merico/aider/files/remove/command.py
Normal file
65
merico/aider/files/remove/command.py
Normal file
@ -0,0 +1,65 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
def is_valid_path(path):
|
||||
"""
|
||||
检查路径是否为有效的文件路径形式
|
||||
"""
|
||||
try:
|
||||
# 尝试规范化路径
|
||||
normalized_path = os.path.normpath(path)
|
||||
# 检查路径是否是绝对路径或相对路径
|
||||
return os.path.isabs(normalized_path) or not os.path.dirname(normalized_path) == normalized_path
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def remove_file(file_path):
|
||||
# 1. 检查是否为有效的文件路径形式
|
||||
if not is_valid_path(file_path):
|
||||
print(f"Error: '{file_path}' is not a valid file path format.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 获取绝对路径
|
||||
abs_file_path = file_path.strip()
|
||||
|
||||
# 2. 从.chat/.aider_files文件中移除指定文件路径
|
||||
aider_files_path = os.path.join('.chat', '.aider_files')
|
||||
|
||||
# 确保.chat目录存在
|
||||
if not os.path.exists(aider_files_path):
|
||||
print(f"Error: '{aider_files_path}' does not exist.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 读取现有文件列表
|
||||
existing_files = set()
|
||||
with open(aider_files_path, 'r') as f:
|
||||
existing_files = set(line.strip() for line in f)
|
||||
|
||||
# 检查文件是否在列表中
|
||||
if abs_file_path not in existing_files:
|
||||
print(f"'{abs_file_path}' is not in aider files.")
|
||||
sys.exit(0)
|
||||
|
||||
# 移除文件
|
||||
existing_files.remove(abs_file_path)
|
||||
|
||||
# 写入更新后的文件列表
|
||||
with open(aider_files_path, 'w') as f:
|
||||
for file in sorted(existing_files):
|
||||
f.write(f"{file}\n")
|
||||
|
||||
print(f"Removed '{abs_file_path}' from aider files.")
|
||||
print("\nCurrent aider files:")
|
||||
for file in sorted(existing_files):
|
||||
print(f"- {file}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2 or sys.argv[1].strip() == "":
|
||||
print("Usage: /aider.files.remove <file_path>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
remove_file(file_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
4
merico/aider/files/remove/command.yml
Normal file
4
merico/aider/files/remove/command.yml
Normal file
@ -0,0 +1,4 @@
|
||||
description: "remove files from aider"
|
||||
input: required
|
||||
steps:
|
||||
- run: $devchat_python $command_path/command.py "$input"
|
2
merico/aider/requirements.txt
Normal file
2
merico/aider/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
git+https://gitee.com/imlaji/aider.git@main
|
||||
git+https://gitee.com/devchat-ai/devchat.git@aider
|
Loading…
x
Reference in New Issue
Block a user