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