refactor: 重构现代化命令管理为基于配置文件的安全方案
🎯 解决问题: - 避免推荐用户系统中不存在的工具 - 防止因缺失工具导致的命令执行失败 - 提供更安全、更灵活的现代化命令管理 🔧 主要改进: - 新增 ai_shell/modern_commands.toml 配置文件 - 智能检测系统中已安装的现代化工具 - 只推荐实际可用的工具,安全回退到原始命令 - 完整的工具描述、分类和安装提示 📦 配置文件特性: - 28 个命令映射配置 - 20 个工具描述说明 - 8 个工具分类组织 - 6 个详细安装提示 🛠️ 新增管理工具: - scripts/manage_modern_commands.py 配置管理脚本 - 支持验证、列表、安装建议等功能 - 完整的配置状态检查和报告 🔍 用户体验优化: - ai --config 显示详细的工具状态 - 区分已启用、保持原样、未安装的工具 - 提供具体的安装命令和说明 - 支持环境变量和配置文件自定义 🛡️ 安全保障: - 绝不推荐不存在的工具 - 优雅降级到原始命令 - 保持完全向后兼容性 📋 技术实现: - 添加 tomli 依赖支持 TOML 解析 - 重构配置加载逻辑 - 智能工具检测和状态管理 - 完善的错误处理和回退机制
This commit is contained in:
@ -3,13 +3,23 @@ Configuration module for AI Shell
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
except ImportError:
|
||||
load_dotenv = None
|
||||
|
||||
try:
|
||||
import tomllib # Python 3.11+
|
||||
except ImportError:
|
||||
try:
|
||||
import tomli as tomllib # fallback for older Python
|
||||
except ImportError:
|
||||
tomllib = None
|
||||
|
||||
# Load .env file if it exists
|
||||
def load_env_file() -> None:
|
||||
"""Load environment variables from .env file"""
|
||||
@ -61,57 +71,30 @@ def get_max_retries() -> int:
|
||||
"""Get max retries from environment"""
|
||||
return int(os.getenv("AI_SHELL_MAX_RETRIES", "3"))
|
||||
|
||||
def get_modern_commands() -> dict:
|
||||
"""Get modern command alternatives configuration"""
|
||||
# Default modern command alternatives
|
||||
default_alternatives = {
|
||||
"ls": "eza", # Modern ls replacement
|
||||
"cat": "bat", # Syntax highlighting cat
|
||||
"find": "fd", # Fast and user-friendly find
|
||||
"grep": "rg", # Ripgrep - faster grep
|
||||
"du": "ncdu", # Interactive disk usage
|
||||
"df": "duf", # Modern df with better output
|
||||
"ps": "procs", # Modern ps replacement
|
||||
"top": "htop", # Interactive process viewer
|
||||
"ping": "gping", # Ping with graph
|
||||
"curl": "httpie", # User-friendly HTTP client
|
||||
"wget": "aria2c", # Multi-connection downloader
|
||||
"diff": "delta", # Better diff with syntax highlighting
|
||||
"tree": "tree", # Keep tree as is (already modern)
|
||||
"sed": "sd", # Simpler sed alternative
|
||||
"awk": "choose", # Human-friendly awk alternative
|
||||
"cut": "choose", # Alternative to cut
|
||||
"sort": "sort", # Keep sort as is
|
||||
"uniq": "uniq", # Keep uniq as is
|
||||
"head": "head", # Keep head as is
|
||||
"tail": "tail", # Keep tail as is
|
||||
"less": "bat", # Use bat for paging too
|
||||
"more": "bat", # Use bat for paging
|
||||
"vim": "nvim", # Modern vim
|
||||
"nano": "micro", # Modern nano alternative
|
||||
"cd": "zoxide", # Smart cd with frecency
|
||||
"cp": "cp", # Keep cp as is (or could use rsync)
|
||||
"mv": "mv", # Keep mv as is
|
||||
"rm": "trash", # Safer deletion (trash-cli)
|
||||
"mkdir": "mkdir", # Keep mkdir as is
|
||||
"rmdir": "rmdir", # Keep rmdir as is
|
||||
"chmod": "chmod", # Keep chmod as is
|
||||
"chown": "chown", # Keep chown as is
|
||||
"tar": "ouch", # Universal archive tool
|
||||
"zip": "ouch", # Universal archive tool
|
||||
"unzip": "ouch", # Universal archive tool
|
||||
"ssh": "ssh", # Keep ssh as is
|
||||
"scp": "rsync", # More efficient file transfer
|
||||
"rsync": "rsync", # Keep rsync as is
|
||||
"git": "git", # Keep git as is
|
||||
"docker": "docker", # Keep docker as is
|
||||
"python": "python", # Keep python as is
|
||||
"node": "node", # Keep node as is
|
||||
"npm": "pnpm", # Faster npm alternative
|
||||
"yarn": "pnpm", # Faster yarn alternative
|
||||
}
|
||||
def load_modern_commands_config() -> Dict[str, any]:
|
||||
"""Load modern commands configuration from TOML file"""
|
||||
config_file = Path(__file__).parent / "modern_commands.toml"
|
||||
|
||||
# Try to load custom alternatives from environment or config file
|
||||
if not config_file.exists():
|
||||
return {"commands": {}, "descriptions": {}, "categories": {}, "installation_hints": {}}
|
||||
|
||||
if tomllib is None:
|
||||
# Fallback to basic parsing if tomllib not available
|
||||
return {"commands": {}, "descriptions": {}, "categories": {}, "installation_hints": {}}
|
||||
|
||||
try:
|
||||
with open(config_file, "rb") as f:
|
||||
return tomllib.load(f)
|
||||
except Exception:
|
||||
return {"commands": {}, "descriptions": {}, "categories": {}, "installation_hints": {}}
|
||||
|
||||
def get_modern_commands() -> Dict[str, str]:
|
||||
"""Get modern command alternatives configuration"""
|
||||
# Load from TOML config file
|
||||
config = load_modern_commands_config()
|
||||
default_alternatives = config.get("commands", {})
|
||||
|
||||
# Try to load custom alternatives from environment
|
||||
custom_alternatives_str = os.getenv("AI_SHELL_MODERN_COMMANDS", "")
|
||||
custom_alternatives = {}
|
||||
|
||||
@ -132,10 +115,8 @@ def get_modern_commands() -> dict:
|
||||
|
||||
return alternatives
|
||||
|
||||
def get_available_modern_commands() -> dict:
|
||||
def get_available_modern_commands() -> Dict[str, str]:
|
||||
"""Get only the modern commands that are actually installed on the system"""
|
||||
import shutil
|
||||
|
||||
all_alternatives = get_modern_commands()
|
||||
available_alternatives = {}
|
||||
|
||||
@ -143,17 +124,45 @@ def get_available_modern_commands() -> dict:
|
||||
# Check if the modern command is actually available
|
||||
if shutil.which(new_cmd):
|
||||
available_alternatives[old_cmd] = new_cmd
|
||||
# If modern command not available, keep the original
|
||||
# If modern command not available, keep the original (if it exists)
|
||||
elif shutil.which(old_cmd):
|
||||
available_alternatives[old_cmd] = old_cmd
|
||||
|
||||
return available_alternatives
|
||||
|
||||
def get_missing_modern_commands() -> Dict[str, str]:
|
||||
"""Get modern commands that are configured but not installed"""
|
||||
all_alternatives = get_modern_commands()
|
||||
missing_commands = {}
|
||||
|
||||
for old_cmd, new_cmd in all_alternatives.items():
|
||||
# If modern command is not available but old command exists
|
||||
if not shutil.which(new_cmd) and shutil.which(old_cmd) and old_cmd != new_cmd:
|
||||
missing_commands[old_cmd] = new_cmd
|
||||
|
||||
return missing_commands
|
||||
|
||||
def get_installation_hint(command: str) -> Optional[str]:
|
||||
"""Get installation hint for a modern command"""
|
||||
config = load_modern_commands_config()
|
||||
installation_hints = config.get("installation_hints", {})
|
||||
return installation_hints.get(command)
|
||||
|
||||
def get_command_description(command: str) -> Optional[str]:
|
||||
"""Get description for a modern command"""
|
||||
config = load_modern_commands_config()
|
||||
descriptions = config.get("descriptions", {})
|
||||
return descriptions.get(command)
|
||||
|
||||
def generate_modern_commands_prompt() -> str:
|
||||
"""Generate prompt text about available modern command alternatives"""
|
||||
available_commands = get_available_modern_commands()
|
||||
config = load_modern_commands_config()
|
||||
|
||||
if not available_commands:
|
||||
# Only include commands that have modern alternatives available
|
||||
active_alternatives = {k: v for k, v in available_commands.items() if k != v}
|
||||
|
||||
if not active_alternatives:
|
||||
return ""
|
||||
|
||||
prompt_parts = [
|
||||
@ -162,42 +171,44 @@ def generate_modern_commands_prompt() -> str:
|
||||
""
|
||||
]
|
||||
|
||||
# Group by categories for better readability
|
||||
categories = {
|
||||
"文件操作": ["ls", "cat", "find", "tree", "cp", "mv", "rm"],
|
||||
"文本处理": ["grep", "sed", "awk", "cut", "sort", "uniq", "head", "tail", "less", "more", "diff"],
|
||||
"系统监控": ["ps", "top", "du", "df"],
|
||||
"网络工具": ["ping", "curl", "wget"],
|
||||
"编辑器": ["vim", "nano"],
|
||||
"导航": ["cd"],
|
||||
"压缩工具": ["tar", "zip", "unzip"],
|
||||
"包管理": ["npm", "yarn"],
|
||||
"其他": []
|
||||
}
|
||||
# Use categories from config file
|
||||
categories = config.get("categories", {})
|
||||
descriptions = config.get("descriptions", {})
|
||||
|
||||
for category, commands in categories.items():
|
||||
for category_name, commands in categories.items():
|
||||
category_commands = []
|
||||
for cmd in commands:
|
||||
if cmd in available_commands and available_commands[cmd] != cmd:
|
||||
category_commands.append(f" - Use `{available_commands[cmd]}` instead of `{cmd}`")
|
||||
if cmd in active_alternatives:
|
||||
new_cmd = active_alternatives[cmd]
|
||||
desc = descriptions.get(new_cmd, "")
|
||||
if desc:
|
||||
category_commands.append(f" - Use `{new_cmd}` instead of `{cmd}` ({desc})")
|
||||
else:
|
||||
category_commands.append(f" - Use `{new_cmd}` instead of `{cmd}`")
|
||||
|
||||
if category_commands:
|
||||
prompt_parts.append(f"**{category}:**")
|
||||
# Convert category name to display format
|
||||
display_name = category_name.replace("_", " ").title()
|
||||
prompt_parts.append(f"**{display_name}:**")
|
||||
prompt_parts.extend(category_commands)
|
||||
prompt_parts.append("")
|
||||
|
||||
# Add remaining commands not in categories
|
||||
other_commands = []
|
||||
categorized_commands = set()
|
||||
for commands in categories.values():
|
||||
categorized_commands.update(commands)
|
||||
|
||||
for old_cmd, new_cmd in available_commands.items():
|
||||
if old_cmd not in categorized_commands and new_cmd != old_cmd:
|
||||
other_commands.append(f" - Use `{new_cmd}` instead of `{old_cmd}`")
|
||||
other_commands = []
|
||||
for old_cmd, new_cmd in active_alternatives.items():
|
||||
if old_cmd not in categorized_commands:
|
||||
desc = descriptions.get(new_cmd, "")
|
||||
if desc:
|
||||
other_commands.append(f" - Use `{new_cmd}` instead of `{old_cmd}` ({desc})")
|
||||
else:
|
||||
other_commands.append(f" - Use `{new_cmd}` instead of `{old_cmd}`")
|
||||
|
||||
if other_commands:
|
||||
prompt_parts.append("**其他工具:**")
|
||||
prompt_parts.append("**Other Tools:**")
|
||||
prompt_parts.extend(other_commands)
|
||||
prompt_parts.append("")
|
||||
|
||||
|
@ -81,7 +81,8 @@ def create_parser() -> argparse.ArgumentParser:
|
||||
def show_config() -> None:
|
||||
"""Show current configuration"""
|
||||
from .config import (get_api_key, get_base_url, get_model, get_timeout,
|
||||
get_max_retries, validate_config, get_available_modern_commands)
|
||||
get_max_retries, validate_config, get_available_modern_commands,
|
||||
get_missing_modern_commands, get_installation_hint)
|
||||
|
||||
print("AI Shell Configuration:")
|
||||
print(f" Model: {get_model()}")
|
||||
@ -115,26 +116,40 @@ def show_config() -> None:
|
||||
|
||||
# Show modern commands configuration
|
||||
modern_commands = get_available_modern_commands()
|
||||
if modern_commands:
|
||||
print(f"\n现代化命令替代 ({len(modern_commands)} 个可用):")
|
||||
missing_commands = get_missing_modern_commands()
|
||||
|
||||
print(f"\n现代化命令替代:")
|
||||
|
||||
if modern_commands:
|
||||
# Group commands for better display
|
||||
active_alternatives = {k: v for k, v in modern_commands.items() if k != v}
|
||||
unchanged_commands = {k: v for k, v in modern_commands.items() if k == v}
|
||||
|
||||
if active_alternatives:
|
||||
print(" 已启用的替代:")
|
||||
print(f" ✅ 已启用的替代 ({len(active_alternatives)} 个):")
|
||||
for old_cmd, new_cmd in sorted(active_alternatives.items()):
|
||||
print(f" {old_cmd} → {new_cmd}")
|
||||
|
||||
if unchanged_commands:
|
||||
print(f" 保持原样: {', '.join(sorted(unchanged_commands.keys()))}")
|
||||
print(f" ➡️ 保持原样: {', '.join(sorted(unchanged_commands.keys()))}")
|
||||
|
||||
print("\n 自定义配置格式:")
|
||||
print(" export AI_SHELL_MODERN_COMMANDS=\"old1:new1,old2:new2\"")
|
||||
print(" 例如: export AI_SHELL_MODERN_COMMANDS=\"ls:exa,cat:bat,find:fd\"")
|
||||
else:
|
||||
print("\n现代化命令替代: 未检测到可用的现代化工具")
|
||||
if missing_commands:
|
||||
print(f"\n ⚠️ 可配置但未安装的工具 ({len(missing_commands)} 个):")
|
||||
for old_cmd, new_cmd in sorted(missing_commands.items()):
|
||||
hint = get_installation_hint(new_cmd)
|
||||
if hint:
|
||||
print(f" {old_cmd} → {new_cmd}")
|
||||
print(f" 安装: {hint.split('#')[0].strip()}")
|
||||
else:
|
||||
print(f" {old_cmd} → {new_cmd}")
|
||||
|
||||
if not modern_commands and not missing_commands:
|
||||
print(" 未检测到可用的现代化工具配置")
|
||||
|
||||
print("\n 配置方法:")
|
||||
print(" 1. 编辑配置文件: ai_shell/modern_commands.toml")
|
||||
print(" 2. 环境变量: export AI_SHELL_MODERN_COMMANDS=\"ls:eza,cat:bat\"")
|
||||
print(" 3. .env 文件: AI_SHELL_MODERN_COMMANDS=\"ls:eza,cat:bat\"")
|
||||
|
||||
def main() -> None:
|
||||
"""Main entry point"""
|
||||
|
98
ai_shell/modern_commands.toml
Normal file
98
ai_shell/modern_commands.toml
Normal file
@ -0,0 +1,98 @@
|
||||
# AI Shell 现代化命令替代配置文件
|
||||
# 只有在系统中实际安装了对应工具时,才会启用替代
|
||||
|
||||
[commands]
|
||||
# 文件操作
|
||||
ls = "eza" # 现代化的 ls,支持图标和颜色
|
||||
cat = "bat" # 带语法高亮的 cat
|
||||
find = "fd" # 更快更友好的 find
|
||||
tree = "tree" # 保持原样(已经很现代)
|
||||
|
||||
# 文本处理
|
||||
grep = "rg" # ripgrep - 更快的文本搜索
|
||||
sed = "sd" # 更简单的 sed 替代
|
||||
awk = "choose" # 人性化的 awk 替代
|
||||
cut = "choose" # cut 的替代
|
||||
diff = "delta" # 带语法高亮的 diff
|
||||
less = "bat" # 用 bat 进行分页显示
|
||||
more = "bat" # 用 bat 进行分页显示
|
||||
|
||||
# 系统监控
|
||||
ps = "procs" # 现代化的进程查看器
|
||||
top = "htop" # 交互式进程监控
|
||||
du = "ncdu" # 交互式磁盘使用分析
|
||||
df = "duf" # 更好的磁盘空间显示
|
||||
|
||||
# 网络工具
|
||||
ping = "gping" # 带图形的 ping
|
||||
curl = "httpie" # 更友好的 HTTP 客户端
|
||||
wget = "aria2c" # 多连接下载器
|
||||
|
||||
# 编辑器
|
||||
vim = "nvim" # 现代化的 vim
|
||||
nano = "micro" # 现代化的 nano 替代
|
||||
|
||||
# 导航和文件管理
|
||||
cd = "zoxide" # 智能目录跳转(需要初始化)
|
||||
rm = "trash" # 安全删除(移到回收站)
|
||||
|
||||
# 压缩工具
|
||||
tar = "ouch" # 通用压缩工具
|
||||
zip = "ouch" # 通用压缩工具
|
||||
unzip = "ouch" # 通用压缩工具
|
||||
|
||||
# 开发工具
|
||||
npm = "pnpm" # 更快的 npm 替代
|
||||
yarn = "pnpm" # 更快的 yarn 替代
|
||||
scp = "rsync" # 更高效的文件传输
|
||||
|
||||
# 保持原样的命令(不需要替代)
|
||||
# git = "git"
|
||||
# docker = "docker"
|
||||
# python = "python"
|
||||
# node = "node"
|
||||
# ssh = "ssh"
|
||||
# rsync = "rsync"
|
||||
|
||||
[descriptions]
|
||||
# 工具描述,用于生成更好的 AI 提示
|
||||
eza = "现代化的 ls 替代,支持图标、颜色和更好的格式化"
|
||||
bat = "带语法高亮和行号的 cat 替代"
|
||||
fd = "更快、更用户友好的 find 替代"
|
||||
rg = "极快的文本搜索工具,比 grep 更快"
|
||||
sd = "更简单直观的 sed 替代"
|
||||
choose = "人性化的字段选择工具,可替代 awk 和 cut"
|
||||
delta = "带语法高亮的 git diff 查看器"
|
||||
procs = "现代化的进程查看器,比 ps 更直观"
|
||||
htop = "交互式的系统监控工具"
|
||||
ncdu = "交互式的磁盘使用分析工具"
|
||||
duf = "更美观的磁盘空间显示工具"
|
||||
gping = "带图形显示的 ping 工具"
|
||||
httpie = "更友好的 HTTP 客户端"
|
||||
aria2c = "支持多连接的下载工具"
|
||||
nvim = "现代化的 vim 编辑器"
|
||||
micro = "现代化的终端文本编辑器"
|
||||
zoxide = "智能的目录跳转工具"
|
||||
trash = "安全的文件删除工具(移到回收站)"
|
||||
ouch = "通用的压缩/解压工具"
|
||||
pnpm = "更快、更高效的 Node.js 包管理器"
|
||||
|
||||
[categories]
|
||||
# 工具分类,用于更好的组织和显示
|
||||
file_operations = ["ls", "cat", "find", "tree", "rm"]
|
||||
text_processing = ["grep", "sed", "awk", "cut", "diff", "less", "more"]
|
||||
system_monitoring = ["ps", "top", "du", "df"]
|
||||
network_tools = ["ping", "curl", "wget"]
|
||||
editors = ["vim", "nano"]
|
||||
navigation = ["cd"]
|
||||
compression = ["tar", "zip", "unzip"]
|
||||
development = ["npm", "yarn", "scp"]
|
||||
|
||||
[installation_hints]
|
||||
# 安装提示,当工具不存在时可以提供安装建议
|
||||
eza = "brew install eza # macOS\nsudo apt install eza # Ubuntu\ncargo install eza # Rust"
|
||||
bat = "brew install bat # macOS\nsudo apt install bat # Ubuntu\ncargo install bat # Rust"
|
||||
fd = "brew install fd # macOS\nsudo apt install fd-find # Ubuntu\ncargo install fd-find # Rust"
|
||||
rg = "brew install ripgrep # macOS\nsudo apt install ripgrep # Ubuntu\ncargo install ripgrep # Rust"
|
||||
ncdu = "brew install ncdu # macOS\nsudo apt install ncdu # Ubuntu"
|
||||
htop = "brew install htop # macOS\nsudo apt install htop # Ubuntu"
|
@ -23,6 +23,7 @@ dependencies = [
|
||||
"openai",
|
||||
"requests>=2.32.4",
|
||||
"python-dotenv>=1.0.0",
|
||||
"tomli>=1.2.0; python_version<'3.11'",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
200
scripts/manage_modern_commands.py
Executable file
200
scripts/manage_modern_commands.py
Executable file
@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
现代化命令配置管理工具
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
from ai_shell.config import (
|
||||
load_modern_commands_config,
|
||||
get_modern_commands,
|
||||
get_available_modern_commands,
|
||||
get_missing_modern_commands,
|
||||
get_installation_hint,
|
||||
get_command_description
|
||||
)
|
||||
|
||||
def list_commands():
|
||||
"""列出所有配置的现代化命令"""
|
||||
print("🔧 现代化命令配置状态\n")
|
||||
|
||||
available = get_available_modern_commands()
|
||||
missing = get_missing_modern_commands()
|
||||
|
||||
# 已启用的替代
|
||||
active_alternatives = {k: v for k, v in available.items() if k != v}
|
||||
if active_alternatives:
|
||||
print(f"✅ 已启用的替代 ({len(active_alternatives)} 个):")
|
||||
for old_cmd, new_cmd in sorted(active_alternatives.items()):
|
||||
desc = get_command_description(new_cmd)
|
||||
if desc:
|
||||
print(f" {old_cmd:8} → {new_cmd:12} ({desc})")
|
||||
else:
|
||||
print(f" {old_cmd:8} → {new_cmd}")
|
||||
print()
|
||||
|
||||
# 保持原样的命令
|
||||
unchanged = {k: v for k, v in available.items() if k == v}
|
||||
if unchanged:
|
||||
print(f"➡️ 保持原样的命令: {', '.join(sorted(unchanged.keys()))}\n")
|
||||
|
||||
# 未安装的工具
|
||||
if missing:
|
||||
print(f"⚠️ 可配置但未安装的工具 ({len(missing)} 个):")
|
||||
for old_cmd, new_cmd in sorted(missing.items()):
|
||||
desc = get_command_description(new_cmd)
|
||||
hint = get_installation_hint(new_cmd)
|
||||
|
||||
if desc:
|
||||
print(f" {old_cmd:8} → {new_cmd:12} ({desc})")
|
||||
else:
|
||||
print(f" {old_cmd:8} → {new_cmd}")
|
||||
|
||||
if hint:
|
||||
print(f" 安装: {hint.split('#')[0].strip()}")
|
||||
print()
|
||||
|
||||
def check_command(command):
|
||||
"""检查特定命令的状态"""
|
||||
config = load_modern_commands_config()
|
||||
commands = config.get("commands", {})
|
||||
|
||||
if command not in commands:
|
||||
print(f"❌ 命令 '{command}' 未在配置中找到")
|
||||
return
|
||||
|
||||
modern_cmd = commands[command]
|
||||
desc = get_command_description(modern_cmd)
|
||||
hint = get_installation_hint(modern_cmd)
|
||||
|
||||
print(f"🔍 命令 '{command}' 的配置:")
|
||||
print(f" 替代工具: {modern_cmd}")
|
||||
|
||||
if desc:
|
||||
print(f" 描述: {desc}")
|
||||
|
||||
# 检查是否安装
|
||||
if shutil.which(modern_cmd):
|
||||
print(f" 状态: ✅ 已安装")
|
||||
print(f" 路径: {shutil.which(modern_cmd)}")
|
||||
else:
|
||||
print(f" 状态: ❌ 未安装")
|
||||
if hint:
|
||||
print(f" 安装方法: {hint}")
|
||||
|
||||
def install_suggestions():
|
||||
"""显示安装建议"""
|
||||
missing = get_missing_modern_commands()
|
||||
|
||||
if not missing:
|
||||
print("✅ 所有配置的现代化工具都已安装!")
|
||||
return
|
||||
|
||||
print("📦 安装建议\n")
|
||||
|
||||
# 按类别分组显示
|
||||
config = load_modern_commands_config()
|
||||
categories = config.get("categories", {})
|
||||
|
||||
for category_name, commands in categories.items():
|
||||
category_missing = []
|
||||
for cmd in commands:
|
||||
if cmd in missing:
|
||||
category_missing.append((cmd, missing[cmd]))
|
||||
|
||||
if category_missing:
|
||||
display_name = category_name.replace("_", " ").title()
|
||||
print(f"**{display_name}:**")
|
||||
|
||||
for old_cmd, new_cmd in category_missing:
|
||||
hint = get_installation_hint(new_cmd)
|
||||
desc = get_command_description(new_cmd)
|
||||
|
||||
print(f" {new_cmd}:")
|
||||
if desc:
|
||||
print(f" 功能: {desc}")
|
||||
if hint:
|
||||
print(f" 安装: {hint}")
|
||||
print()
|
||||
|
||||
def validate_config():
|
||||
"""验证配置文件"""
|
||||
config_file = Path(__file__).parent.parent / "ai_shell" / "modern_commands.toml"
|
||||
|
||||
print(f"🔍 验证配置文件: {config_file}")
|
||||
|
||||
if not config_file.exists():
|
||||
print("❌ 配置文件不存在")
|
||||
return False
|
||||
|
||||
try:
|
||||
config = load_modern_commands_config()
|
||||
|
||||
# 检查必需的部分
|
||||
required_sections = ["commands", "descriptions", "categories", "installation_hints"]
|
||||
for section in required_sections:
|
||||
if section not in config:
|
||||
print(f"⚠️ 缺少配置部分: {section}")
|
||||
else:
|
||||
print(f"✅ 配置部分 '{section}': {len(config[section])} 项")
|
||||
|
||||
# 检查命令一致性
|
||||
commands = config.get("commands", {})
|
||||
descriptions = config.get("descriptions", {})
|
||||
installation_hints = config.get("installation_hints", {})
|
||||
|
||||
print(f"\n📊 配置统计:")
|
||||
print(f" 配置的命令: {len(commands)}")
|
||||
print(f" 有描述的工具: {len(descriptions)}")
|
||||
print(f" 有安装提示的工具: {len(installation_hints)}")
|
||||
|
||||
# 检查缺少描述的工具
|
||||
modern_tools = set(commands.values())
|
||||
missing_descriptions = modern_tools - set(descriptions.keys())
|
||||
if missing_descriptions:
|
||||
print(f" ⚠️ 缺少描述的工具: {', '.join(sorted(missing_descriptions))}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 配置文件解析错误: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="现代化命令配置管理工具")
|
||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
||||
|
||||
# list 命令
|
||||
subparsers.add_parser("list", help="列出所有配置的现代化命令")
|
||||
|
||||
# check 命令
|
||||
check_parser = subparsers.add_parser("check", help="检查特定命令的状态")
|
||||
check_parser.add_argument("command", help="要检查的命令名")
|
||||
|
||||
# install 命令
|
||||
subparsers.add_parser("install", help="显示安装建议")
|
||||
|
||||
# validate 命令
|
||||
subparsers.add_parser("validate", help="验证配置文件")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "list":
|
||||
list_commands()
|
||||
elif args.command == "check":
|
||||
check_command(args.command)
|
||||
elif args.command == "install":
|
||||
install_suggestions()
|
||||
elif args.command == "validate":
|
||||
validate_config()
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
3
uv.lock
generated
3
uv.lock
generated
@ -4,7 +4,7 @@ requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "ai-shell"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "openai" },
|
||||
@ -19,6 +19,7 @@ requires-dist = [
|
||||
{ name = "pydantic-ai" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||
{ name = "requests", specifier = ">=2.32.4" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=1.2.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
201
基于配置文件的现代化命令管理完成总结.md
Normal file
201
基于配置文件的现代化命令管理完成总结.md
Normal file
@ -0,0 +1,201 @@
|
||||
# 基于配置文件的现代化命令管理完成总结
|
||||
|
||||
## 🎯 问题解决
|
||||
|
||||
您提出的问题非常重要:**有些用户主机上可能没有这些工具,如果默认设置了一些替代,可能会导致无法运行**。
|
||||
|
||||
我已经完全重构了现代化命令管理系统,采用基于配置文件的安全方案。
|
||||
|
||||
## ✅ 新的解决方案
|
||||
|
||||
### 1. **配置文件驱动**
|
||||
- 创建了 `ai_shell/modern_commands.toml` 配置文件
|
||||
- 所有现代化命令映射都在配置文件中定义
|
||||
- 支持工具描述、分类和安装提示
|
||||
|
||||
### 2. **智能检测机制**
|
||||
- **只推荐已安装的工具**:系统会检测哪些现代化工具实际可用
|
||||
- **安全回退**:如果现代化工具不存在,保持使用原始命令
|
||||
- **避免错误**:绝不会推荐不存在的命令
|
||||
|
||||
### 3. **完整的配置管理**
|
||||
- 详细的配置状态显示
|
||||
- 缺失工具的安装建议
|
||||
- 配置验证和管理工具
|
||||
|
||||
## 📋 技术实现
|
||||
|
||||
### 配置文件结构 (`ai_shell/modern_commands.toml`)
|
||||
|
||||
```toml
|
||||
[commands]
|
||||
# 命令映射
|
||||
ls = "eza"
|
||||
cat = "bat"
|
||||
find = "fd"
|
||||
# ... 更多映射
|
||||
|
||||
[descriptions]
|
||||
# 工具描述
|
||||
eza = "现代化的 ls 替代,支持图标、颜色和更好的格式化"
|
||||
bat = "带语法高亮和行号的 cat 替代"
|
||||
# ... 更多描述
|
||||
|
||||
[categories]
|
||||
# 工具分类
|
||||
file_operations = ["ls", "cat", "find", "tree", "rm"]
|
||||
text_processing = ["grep", "sed", "awk", "cut", "diff"]
|
||||
# ... 更多分类
|
||||
|
||||
[installation_hints]
|
||||
# 安装提示
|
||||
eza = "brew install eza # macOS\nsudo apt install eza # Ubuntu"
|
||||
bat = "brew install bat # macOS\nsudo apt install bat # Ubuntu"
|
||||
# ... 更多提示
|
||||
```
|
||||
|
||||
### 智能检测逻辑
|
||||
|
||||
```python
|
||||
def get_available_modern_commands() -> Dict[str, str]:
|
||||
"""只返回系统中实际可用的现代化命令"""
|
||||
all_alternatives = get_modern_commands()
|
||||
available_alternatives = {}
|
||||
|
||||
for old_cmd, new_cmd in all_alternatives.items():
|
||||
# 检查现代化命令是否可用
|
||||
if shutil.which(new_cmd):
|
||||
available_alternatives[old_cmd] = new_cmd
|
||||
# 如果不可用,保持原始命令(如果存在)
|
||||
elif shutil.which(old_cmd):
|
||||
available_alternatives[old_cmd] = old_cmd
|
||||
|
||||
return available_alternatives
|
||||
```
|
||||
|
||||
## 🔧 用户体验
|
||||
|
||||
### 配置状态显示
|
||||
|
||||
```bash
|
||||
ai --config
|
||||
```
|
||||
|
||||
输出示例:
|
||||
```
|
||||
现代化命令替代:
|
||||
✅ 已启用的替代 (5 个):
|
||||
find → fd
|
||||
npm → pnpm
|
||||
rm → trash
|
||||
scp → rsync
|
||||
yarn → pnpm
|
||||
|
||||
⚠️ 可配置但未安装的工具 (21 个):
|
||||
cat → bat
|
||||
安装: brew install bat
|
||||
ls → eza
|
||||
安装: brew install eza
|
||||
# ... 更多工具和安装提示
|
||||
```
|
||||
|
||||
### 管理工具
|
||||
|
||||
```bash
|
||||
# 验证配置文件
|
||||
uv run python scripts/manage_modern_commands.py validate
|
||||
|
||||
# 列出所有配置状态
|
||||
uv run python scripts/manage_modern_commands.py list
|
||||
|
||||
# 显示安装建议
|
||||
uv run python scripts/manage_modern_commands.py install
|
||||
|
||||
# 检查特定命令
|
||||
uv run python scripts/manage_modern_commands.py check ls
|
||||
```
|
||||
|
||||
## 🛡️ 安全保障
|
||||
|
||||
### 1. **绝不推荐不存在的工具**
|
||||
- 系统启动时检测所有工具的可用性
|
||||
- AI 提示词中只包含实际可用的替代
|
||||
- 用户永远不会收到无法执行的命令
|
||||
|
||||
### 2. **优雅降级**
|
||||
- 如果现代化工具不存在,自动使用原始命令
|
||||
- 保持系统的完全兼容性
|
||||
- 不影响基本功能
|
||||
|
||||
### 3. **清晰的状态反馈**
|
||||
- 明确显示哪些工具已启用
|
||||
- 列出可配置但未安装的工具
|
||||
- 提供具体的安装指导
|
||||
|
||||
## 📊 配置统计
|
||||
|
||||
当前配置包含:
|
||||
- **28 个命令映射**
|
||||
- **20 个工具描述**
|
||||
- **8 个工具分类**
|
||||
- **6 个安装提示**
|
||||
|
||||
支持的现代化工具类别:
|
||||
- **文件操作**: eza, bat, fd, tree, trash
|
||||
- **文本处理**: rg, sd, choose, delta
|
||||
- **系统监控**: procs, htop, ncdu, duf
|
||||
- **网络工具**: gping, httpie, aria2c
|
||||
- **编辑器**: nvim, micro
|
||||
- **其他**: zoxide, ouch, pnpm
|
||||
|
||||
## 🎯 用户价值
|
||||
|
||||
### 1. **安全性**
|
||||
- 不会推荐不存在的工具
|
||||
- 避免命令执行失败
|
||||
- 保持系统稳定性
|
||||
|
||||
### 2. **灵活性**
|
||||
- 用户可以选择安装感兴趣的工具
|
||||
- 支持自定义配置
|
||||
- 渐进式采用现代化工具
|
||||
|
||||
### 3. **指导性**
|
||||
- 提供安装建议和说明
|
||||
- 展示工具的优势和特性
|
||||
- 帮助用户了解现代化选择
|
||||
|
||||
### 4. **可维护性**
|
||||
- 配置文件易于维护和扩展
|
||||
- 支持版本控制
|
||||
- 便于社区贡献
|
||||
|
||||
## 🚀 使用建议
|
||||
|
||||
### 对于新用户
|
||||
1. 运行 `ai --config` 查看当前状态
|
||||
2. 根据安装提示安装感兴趣的工具
|
||||
3. 重新运行 `ai --config` 验证新工具
|
||||
|
||||
### 对于高级用户
|
||||
1. 编辑 `ai_shell/modern_commands.toml` 自定义配置
|
||||
2. 使用环境变量覆盖特定映射
|
||||
3. 使用管理工具验证和维护配置
|
||||
|
||||
### 对于开发者
|
||||
1. 使用 `scripts/manage_modern_commands.py` 管理配置
|
||||
2. 添加新的工具映射和描述
|
||||
3. 贡献安装提示和最佳实践
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
这个新的基于配置文件的方案完美解决了您提出的问题:
|
||||
|
||||
✅ **安全性**: 只推荐已安装的工具,避免执行失败
|
||||
✅ **灵活性**: 用户可以选择性安装和配置工具
|
||||
✅ **可维护性**: 配置文件易于管理和扩展
|
||||
✅ **用户友好**: 提供清晰的状态显示和安装指导
|
||||
|
||||
现在用户可以安全地使用 AI Shell,无论他们的系统中安装了哪些工具!🚀
|
Reference in New Issue
Block a user