feat: 增强编码能力 - 新增代码编辑器、代码搜索、Git集成
- 新增 plugins/code_editor.py: 精准代码编辑(搜索替换、批量编辑、diff对比) - 新增 plugins/code_search.py: 代码搜索与理解(跨文件搜索、符号提取、依赖分析) - 新增 plugins/git_tools.py: Git 版本控制(status/diff/log/commit/branch) - 新增 core/ 模块: 配置管理、引擎管理、对话处理、显示输出、代码沙箱 - 修复 requirements.txt: 使用正确的 pip 包名(Pillow、opencv-python 等) - 更新 unified_tool_manager.py: 兼容 Plugin 和 Liugin 两种类名 - 更新系统提示词: 增强编程工作流指导 本次更新由 mimo 自动化执行,与开发者无关。
This commit is contained in:
commit
e22652fbbb
53 files changed
+20067
No files matched your search
@@ -0,0 +1,670 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
File Operations Script for file-manager skill
|
||||
Provides file system operations as JSON output
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
|
||||
def json_response(success: bool, operation: str, data: Any = None, error: str = None) -> str:
|
||||
"""Generate JSON response"""
|
||||
return json.dumps({
|
||||
"success": success,
|
||||
"operation": operation,
|
||||
"data": data,
|
||||
"error": error,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def list_directory(path: str, recursive: bool = False, pattern: str = None) -> str:
|
||||
"""List directory contents"""
|
||||
try:
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.exists(path):
|
||||
return json_response(False, "list", error=f"Path not found: {path}")
|
||||
|
||||
if not os.path.isdir(path):
|
||||
return json_response(False, "list", error=f"Not a directory: {path}")
|
||||
|
||||
files = []
|
||||
directories = []
|
||||
|
||||
if recursive:
|
||||
for root, dirs, filenames in os.walk(path):
|
||||
for d in dirs:
|
||||
rel_path = os.path.relpath(os.path.join(root, d), path)
|
||||
if pattern is None or Path(d).match(pattern):
|
||||
directories.append(rel_path)
|
||||
for f in filenames:
|
||||
rel_path = os.path.relpath(os.path.join(root, f), path)
|
||||
if pattern is None or Path(f).match(pattern):
|
||||
files.append(rel_path)
|
||||
else:
|
||||
for item in os.listdir(path):
|
||||
full_path = os.path.join(path, item)
|
||||
if os.path.isdir(full_path):
|
||||
if pattern is None or Path(item).match(pattern):
|
||||
directories.append(item)
|
||||
else:
|
||||
if pattern is None or Path(item).match(pattern):
|
||||
files.append(item)
|
||||
|
||||
return json_response(True, "list", {
|
||||
"path": path,
|
||||
"files": sorted(files),
|
||||
"directories": sorted(directories),
|
||||
"file_count": len(files),
|
||||
"dir_count": len(directories),
|
||||
"total_count": len(files) + len(directories)
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "list", error=str(e))
|
||||
|
||||
|
||||
def read_file(file_path: str, encoding: str = "utf-8", lines: int = None) -> str:
|
||||
"""Read file content"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "read", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "read", error=f"Not a file: {file_path}")
|
||||
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
with open(file_path, 'r', encoding=encoding) as f:
|
||||
if lines:
|
||||
content_lines = []
|
||||
for i, line in enumerate(f):
|
||||
if i >= lines:
|
||||
break
|
||||
content_lines.append(line.rstrip('\n\r'))
|
||||
content = '\n'.join(content_lines)
|
||||
total_lines = sum(1 for _ in open(file_path, 'r', encoding=encoding))
|
||||
else:
|
||||
content = f.read()
|
||||
total_lines = content.count('\n') + 1
|
||||
|
||||
return json_response(True, "read", {
|
||||
"path": file_path,
|
||||
"content": content,
|
||||
"size": file_size,
|
||||
"lines": total_lines,
|
||||
"displayed_lines": lines if lines else total_lines
|
||||
})
|
||||
except UnicodeDecodeError:
|
||||
return json_response(False, "read", error="Cannot decode file (binary or wrong encoding)")
|
||||
except Exception as e:
|
||||
return json_response(False, "read", error=str(e))
|
||||
|
||||
|
||||
def write_file(file_path: str, content: str, append: bool = False) -> str:
|
||||
"""Write or append to file"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
# Create parent directory if needed
|
||||
parent = os.path.dirname(file_path)
|
||||
if parent and not os.path.exists(parent):
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
|
||||
mode = 'a' if append else 'w'
|
||||
with open(file_path, mode, encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
return json_response(True, "write" if not append else "append", {
|
||||
"path": file_path,
|
||||
"size": len(content.encode('utf-8')),
|
||||
"appended": append
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "write", error=str(e))
|
||||
|
||||
|
||||
def copy_path(source: str, destination: str) -> str:
|
||||
"""Copy file or directory"""
|
||||
try:
|
||||
source = os.path.abspath(source)
|
||||
destination = os.path.abspath(destination)
|
||||
|
||||
if not os.path.exists(source):
|
||||
return json_response(False, "copy", error=f"Source not found: {source}")
|
||||
|
||||
# Create parent directory for destination
|
||||
parent = os.path.dirname(destination)
|
||||
if parent and not os.path.exists(parent):
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
|
||||
if os.path.isfile(source):
|
||||
shutil.copy2(source, destination)
|
||||
else:
|
||||
shutil.copytree(source, destination, dirs_exist_ok=True)
|
||||
|
||||
return json_response(True, "copy", {
|
||||
"source": source,
|
||||
"destination": destination,
|
||||
"type": "file" if os.path.isfile(source) else "directory"
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "copy", error=str(e))
|
||||
|
||||
|
||||
def move_path(source: str, destination: str) -> str:
|
||||
"""Move or rename file or directory"""
|
||||
try:
|
||||
source = os.path.abspath(source)
|
||||
destination = os.path.abspath(destination)
|
||||
|
||||
if not os.path.exists(source):
|
||||
return json_response(False, "move", error=f"Source not found: {source}")
|
||||
|
||||
# Create parent directory for destination
|
||||
parent = os.path.dirname(destination)
|
||||
if parent and not os.path.exists(parent):
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
|
||||
shutil.move(source, destination)
|
||||
|
||||
return json_response(True, "move", {
|
||||
"source": source,
|
||||
"destination": destination
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "move", error=str(e))
|
||||
|
||||
|
||||
def delete_path(path: str, recursive: bool = False) -> str:
|
||||
"""Delete file or directory"""
|
||||
try:
|
||||
path = os.path.abspath(path)
|
||||
|
||||
if not os.path.exists(path):
|
||||
return json_response(False, "delete", error=f"Path not found: {path}")
|
||||
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
item_type = "file"
|
||||
elif os.path.isdir(path):
|
||||
if recursive:
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.rmdir(path) # Only works for empty directories
|
||||
item_type = "directory"
|
||||
|
||||
return json_response(True, "delete", {
|
||||
"path": path,
|
||||
"type": item_type
|
||||
})
|
||||
except OSError as e:
|
||||
if "not empty" in str(e).lower():
|
||||
return json_response(False, "delete", error="Directory not empty. Use --recursive to delete.")
|
||||
return json_response(False, "delete", error=str(e))
|
||||
except Exception as e:
|
||||
return json_response(False, "delete", error=str(e))
|
||||
|
||||
|
||||
def search_files(directory: str, pattern: str = None, content: str = None) -> str:
|
||||
"""Search files by name pattern or content"""
|
||||
try:
|
||||
directory = os.path.abspath(directory)
|
||||
if not os.path.exists(directory):
|
||||
return json_response(False, "search", error=f"Directory not found: {directory}")
|
||||
|
||||
results = []
|
||||
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for filename in files:
|
||||
file_path = os.path.join(root, filename)
|
||||
rel_path = os.path.relpath(file_path, directory)
|
||||
|
||||
# Pattern match
|
||||
if pattern and not Path(filename).match(pattern):
|
||||
continue
|
||||
|
||||
# Content search
|
||||
if content:
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
file_content = f.read()
|
||||
if content not in file_content:
|
||||
continue
|
||||
# Find matching lines
|
||||
matches = []
|
||||
for i, line in enumerate(file_content.split('\n'), 1):
|
||||
if content in line:
|
||||
matches.append({"line": i, "text": line.strip()[:100]})
|
||||
if matches:
|
||||
results.append({
|
||||
"path": rel_path,
|
||||
"matches": matches[:5] # Limit matches per file
|
||||
})
|
||||
except (UnicodeDecodeError, IOError):
|
||||
continue
|
||||
else:
|
||||
results.append({"path": rel_path})
|
||||
|
||||
return json_response(True, "search", {
|
||||
"directory": directory,
|
||||
"pattern": pattern,
|
||||
"content_search": content,
|
||||
"results": results[:100], # Limit results
|
||||
"total": len(results)
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "search", error=str(e))
|
||||
|
||||
|
||||
def get_info(path: str) -> str:
|
||||
"""Get file/directory information"""
|
||||
try:
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.exists(path):
|
||||
return json_response(False, "info", error=f"Path not found: {path}")
|
||||
|
||||
stat_info = os.stat(path)
|
||||
|
||||
info = {
|
||||
"path": path,
|
||||
"name": os.path.basename(path),
|
||||
"type": "directory" if os.path.isdir(path) else "file",
|
||||
"size": stat_info.st_size,
|
||||
"created": datetime.fromtimestamp(stat_info.st_ctime).isoformat(),
|
||||
"modified": datetime.fromtimestamp(stat_info.st_mtime).isoformat(),
|
||||
"accessed": datetime.fromtimestamp(stat_info.st_atime).isoformat(),
|
||||
"permissions": oct(stat_info.st_mode)[-3:]
|
||||
}
|
||||
|
||||
if os.path.isfile(path):
|
||||
info["extension"] = os.path.splitext(path)[1]
|
||||
# Try to detect if text file
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
f.read(1024)
|
||||
info["encoding"] = "utf-8"
|
||||
except UnicodeDecodeError:
|
||||
info["encoding"] = "binary"
|
||||
|
||||
return json_response(True, "info", info)
|
||||
except Exception as e:
|
||||
return json_response(False, "info", error=str(e))
|
||||
|
||||
|
||||
def modify_line(file_path: str, line_number: int, new_content: str) -> str:
|
||||
"""Modify a specific line in a file"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "modify_line", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "modify_line", error=f"Not a file: {file_path}")
|
||||
|
||||
if line_number < 1:
|
||||
return json_response(False, "modify_line", error="Line number must be >= 1")
|
||||
|
||||
# Read all lines
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total_lines = len(lines)
|
||||
|
||||
if line_number > total_lines:
|
||||
return json_response(False, "modify_line",
|
||||
error=f"File has only {total_lines} lines, cannot modify line {line_number}")
|
||||
|
||||
# Get old content for reporting
|
||||
old_content = lines[line_number - 1].rstrip('\n\r')
|
||||
|
||||
# Modify the line (preserve newline if existed)
|
||||
has_newline = lines[line_number - 1].endswith('\n')
|
||||
lines[line_number - 1] = new_content + ('\n' if has_newline else '')
|
||||
|
||||
# Write back
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
return json_response(True, "modify_line", {
|
||||
"path": file_path,
|
||||
"line_number": line_number,
|
||||
"old_content": old_content,
|
||||
"new_content": new_content,
|
||||
"total_lines": total_lines
|
||||
})
|
||||
except UnicodeDecodeError:
|
||||
return json_response(False, "modify_line", error="Cannot modify binary file")
|
||||
except Exception as e:
|
||||
return json_response(False, "modify_line", error=str(e))
|
||||
|
||||
|
||||
def insert_line(file_path: str, line_number: int, content: str) -> str:
|
||||
"""Insert a new line at specified position"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "insert_line", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "insert_line", error=f"Not a file: {file_path}")
|
||||
|
||||
if line_number < 1:
|
||||
return json_response(False, "insert_line", error="Line number must be >= 1")
|
||||
|
||||
# Read all lines
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total_lines = len(lines)
|
||||
|
||||
# Insert at position (can be after last line)
|
||||
insert_pos = min(line_number - 1, total_lines)
|
||||
lines.insert(insert_pos, content + '\n')
|
||||
|
||||
# Write back
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
return json_response(True, "insert_line", {
|
||||
"path": file_path,
|
||||
"line_number": line_number,
|
||||
"inserted_content": content,
|
||||
"total_lines": len(lines)
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "insert_line", error=str(e))
|
||||
|
||||
|
||||
def delete_line(file_path: str, line_number: int) -> str:
|
||||
"""Delete a specific line from a file"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "delete_line", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "delete_line", error=f"Not a file: {file_path}")
|
||||
|
||||
if line_number < 1:
|
||||
return json_response(False, "delete_line", error="Line number must be >= 1")
|
||||
|
||||
# Read all lines
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total_lines = len(lines)
|
||||
|
||||
if line_number > total_lines:
|
||||
return json_response(False, "delete_line",
|
||||
error=f"File has only {total_lines} lines, cannot delete line {line_number}")
|
||||
|
||||
# Get deleted content
|
||||
deleted_content = lines[line_number - 1].rstrip('\n\r')
|
||||
|
||||
# Remove the line
|
||||
del lines[line_number - 1]
|
||||
|
||||
# Write back
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
return json_response(True, "delete_line", {
|
||||
"path": file_path,
|
||||
"line_number": line_number,
|
||||
"deleted_content": deleted_content,
|
||||
"total_lines": len(lines)
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "delete_line", error=str(e))
|
||||
|
||||
|
||||
def modify_lines(file_path: str, modifications: Dict[int, str]) -> str:
|
||||
"""
|
||||
Modify multiple lines at once
|
||||
|
||||
Args:
|
||||
file_path: Path to the file
|
||||
modifications: Dict mapping line numbers (1-based) to new content
|
||||
Example: {2: "new line 2", 5: "new line 5"}
|
||||
"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "modify_lines", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "modify_lines", error=f"Not a file: {file_path}")
|
||||
|
||||
# Read all lines
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total_lines = len(lines)
|
||||
results = []
|
||||
|
||||
# Sort modifications by line number (descending to avoid index shift issues)
|
||||
for line_number in sorted(modifications.keys()):
|
||||
if line_number < 1:
|
||||
results.append({"line": line_number, "success": False, "error": "Line number must be >= 1"})
|
||||
continue
|
||||
|
||||
if line_number > total_lines:
|
||||
results.append({"line": line_number, "success": False,
|
||||
"error": f"File has only {total_lines} lines"})
|
||||
continue
|
||||
|
||||
# Get old content
|
||||
old_content = lines[line_number - 1].rstrip('\n\r')
|
||||
new_content = modifications[line_number]
|
||||
|
||||
# Modify the line
|
||||
has_newline = lines[line_number - 1].endswith('\n')
|
||||
lines[line_number - 1] = new_content + ('\n' if has_newline else '')
|
||||
|
||||
results.append({
|
||||
"line": line_number,
|
||||
"success": True,
|
||||
"old_content": old_content,
|
||||
"new_content": new_content
|
||||
})
|
||||
|
||||
# Write back
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
return json_response(True, "modify_lines", {
|
||||
"path": file_path,
|
||||
"total_lines": total_lines,
|
||||
"modifications_count": len(modifications),
|
||||
"results": results
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "modify_lines", error=str(e))
|
||||
|
||||
|
||||
def replace_text(file_path: str, old_text: str, new_text: str, start_line: int = None, end_line: int = None) -> str:
|
||||
"""
|
||||
Replace all occurrences of text in a file (optionally within a line range)
|
||||
|
||||
Args:
|
||||
file_path: Path to the file
|
||||
old_text: Text to find and replace
|
||||
new_text: Replacement text
|
||||
start_line: Optional start line (1-based, inclusive)
|
||||
end_line: Optional end line (1-based, inclusive)
|
||||
"""
|
||||
try:
|
||||
file_path = os.path.abspath(file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return json_response(False, "replace_text", error=f"File not found: {file_path}")
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return json_response(False, "replace_text", error=f"Not a file: {file_path}")
|
||||
|
||||
# Read all lines
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
total_lines = len(lines)
|
||||
replacements = []
|
||||
|
||||
# Determine range
|
||||
start = max(0, (start_line or 1) - 1)
|
||||
end = min(total_lines, end_line or total_lines)
|
||||
|
||||
for i in range(start, end):
|
||||
line = lines[i]
|
||||
if old_text in line:
|
||||
count = line.count(old_text)
|
||||
lines[i] = line.replace(old_text, new_text)
|
||||
replacements.append({
|
||||
"line": i + 1,
|
||||
"count": count,
|
||||
"old_line": line.rstrip('\n\r'),
|
||||
"new_line": lines[i].rstrip('\n\r')
|
||||
})
|
||||
|
||||
# Write back
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
return json_response(True, "replace_text", {
|
||||
"path": file_path,
|
||||
"old_text": old_text,
|
||||
"new_text": new_text,
|
||||
"line_range": [start + 1, end] if start_line or end_line else None,
|
||||
"total_replacements": sum(r["count"] for r in replacements),
|
||||
"lines_modified": len(replacements),
|
||||
"details": replacements
|
||||
})
|
||||
except Exception as e:
|
||||
return json_response(False, "replace_text", error=str(e))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="File operations for file-manager skill")
|
||||
subparsers = parser.add_subparsers(dest="operation", help="Operation to perform")
|
||||
|
||||
# List
|
||||
list_parser = subparsers.add_parser("list", help="List directory contents")
|
||||
list_parser.add_argument("path", help="Directory path")
|
||||
list_parser.add_argument("-r", "--recursive", action="store_true", help="Recursive listing")
|
||||
list_parser.add_argument("-p", "--pattern", help="File pattern (e.g., *.py)")
|
||||
|
||||
# Read
|
||||
read_parser = subparsers.add_parser("read", help="Read file content")
|
||||
read_parser.add_argument("path", help="File path")
|
||||
read_parser.add_argument("-e", "--encoding", default="utf-8", help="File encoding")
|
||||
read_parser.add_argument("-n", "--lines", type=int, help="Number of lines to read")
|
||||
|
||||
# Write
|
||||
write_parser = subparsers.add_parser("write", help="Write to file")
|
||||
write_parser.add_argument("path", help="File path")
|
||||
write_parser.add_argument("-c", "--content", required=True, help="Content to write")
|
||||
write_parser.add_argument("-a", "--append", action="store_true", help="Append to file")
|
||||
|
||||
# Copy
|
||||
copy_parser = subparsers.add_parser("copy", help="Copy file or directory")
|
||||
copy_parser.add_argument("source", help="Source path")
|
||||
copy_parser.add_argument("destination", help="Destination path")
|
||||
|
||||
# Move
|
||||
move_parser = subparsers.add_parser("move", help="Move file or directory")
|
||||
move_parser.add_argument("source", help="Source path")
|
||||
move_parser.add_argument("destination", help="Destination path")
|
||||
|
||||
# Delete
|
||||
delete_parser = subparsers.add_parser("delete", help="Delete file or directory")
|
||||
delete_parser.add_argument("path", help="Path to delete")
|
||||
delete_parser.add_argument("-r", "--recursive", action="store_true", help="Recursive delete")
|
||||
|
||||
# Search
|
||||
search_parser = subparsers.add_parser("search", help="Search files")
|
||||
search_parser.add_argument("directory", help="Directory to search")
|
||||
search_parser.add_argument("-p", "--pattern", help="File pattern")
|
||||
search_parser.add_argument("-c", "--content", help="Content to search for")
|
||||
|
||||
# Info
|
||||
info_parser = subparsers.add_parser("info", help="Get file info")
|
||||
info_parser.add_argument("path", help="File or directory path")
|
||||
|
||||
# Modify line
|
||||
modify_parser = subparsers.add_parser("modify-line", help="Modify a specific line")
|
||||
modify_parser.add_argument("path", help="File path")
|
||||
modify_parser.add_argument("line", type=int, help="Line number (1-based)")
|
||||
modify_parser.add_argument("content", help="New line content")
|
||||
|
||||
# Insert line
|
||||
insert_parser = subparsers.add_parser("insert-line", help="Insert a new line")
|
||||
insert_parser.add_argument("path", help="File path")
|
||||
insert_parser.add_argument("line", type=int, help="Line number to insert at (1-based)")
|
||||
insert_parser.add_argument("content", help="Line content to insert")
|
||||
|
||||
# Delete line
|
||||
del_line_parser = subparsers.add_parser("delete-line", help="Delete a specific line")
|
||||
del_line_parser.add_argument("path", help="File path")
|
||||
del_line_parser.add_argument("line", type=int, help="Line number to delete (1-based)")
|
||||
|
||||
# Modify multiple lines (JSON format)
|
||||
multi_parser = subparsers.add_parser("modify-lines", help="Modify multiple lines at once")
|
||||
multi_parser.add_argument("path", help="File path")
|
||||
multi_parser.add_argument("-m", "--modifications", required=True,
|
||||
help='JSON dict of line numbers to content, e.g. \'{"2": "new line 2", "5": "new line 5"}\'')
|
||||
|
||||
# Replace text
|
||||
replace_parser = subparsers.add_parser("replace", help="Replace text in file")
|
||||
replace_parser.add_argument("path", help="File path")
|
||||
replace_parser.add_argument("old", help="Text to find and replace")
|
||||
replace_parser.add_argument("new", help="Replacement text")
|
||||
replace_parser.add_argument("--start", type=int, help="Start line (optional)")
|
||||
replace_parser.add_argument("--end", type=int, help="End line (optional)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.operation == "list":
|
||||
print(list_directory(args.path, args.recursive, args.pattern))
|
||||
elif args.operation == "read":
|
||||
print(read_file(args.path, args.encoding, args.lines))
|
||||
elif args.operation == "write":
|
||||
print(write_file(args.path, args.content, args.append))
|
||||
elif args.operation == "copy":
|
||||
print(copy_path(args.source, args.destination))
|
||||
elif args.operation == "move":
|
||||
print(move_path(args.source, args.destination))
|
||||
elif args.operation == "delete":
|
||||
print(delete_path(args.path, args.recursive))
|
||||
elif args.operation == "search":
|
||||
print(search_files(args.directory, args.pattern, args.content))
|
||||
elif args.operation == "info":
|
||||
print(get_info(args.path))
|
||||
elif args.operation == "modify-line":
|
||||
print(modify_line(args.path, args.line, args.content))
|
||||
elif args.operation == "insert-line":
|
||||
print(insert_line(args.path, args.line, args.content))
|
||||
elif args.operation == "delete-line":
|
||||
print(delete_line(args.path, args.line))
|
||||
elif args.operation == "modify-lines":
|
||||
mods = json.loads(args.modifications)
|
||||
mods_int = {int(k): v for k, v in mods.items()}
|
||||
print(modify_lines(args.path, mods_int))
|
||||
elif args.operation == "replace":
|
||||
print(replace_text(args.path, args.old, args.new, args.start, args.end))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,3 @@
|
||||
Line 1
|
||||
MODIFIED LINE
|
||||
Line 3
|
||||
@@ -0,0 +1,5 @@
|
||||
Line 1
|
||||
REPLACED LINE 2
|
||||
Line 3
|
||||
REPLACED LINE 4
|
||||
Line 5
|
||||
Reference in new issue
Block a user