🎯 解决问题: - 避免推荐用户系统中不存在的工具 - 防止因缺失工具导致的命令执行失败 - 提供更安全、更灵活的现代化命令管理 🔧 主要改进: - 新增 ai_shell/modern_commands.toml 配置文件 - 智能检测系统中已安装的现代化工具 - 只推荐实际可用的工具,安全回退到原始命令 - 完整的工具描述、分类和安装提示 📦 配置文件特性: - 28 个命令映射配置 - 20 个工具描述说明 - 8 个工具分类组织 - 6 个详细安装提示 🛠️ 新增管理工具: - scripts/manage_modern_commands.py 配置管理脚本 - 支持验证、列表、安装建议等功能 - 完整的配置状态检查和报告 🔍 用户体验优化: - ai --config 显示详细的工具状态 - 区分已启用、保持原样、未安装的工具 - 提供具体的安装命令和说明 - 支持环境变量和配置文件自定义 🛡️ 安全保障: - 绝不推荐不存在的工具 - 优雅降级到原始命令 - 保持完全向后兼容性 📋 技术实现: - 添加 tomli 依赖支持 TOML 解析 - 重构配置加载逻辑 - 智能工具检测和状态管理 - 完善的错误处理和回退机制
181 lines
5.8 KiB
Python
181 lines
5.8 KiB
Python
"""
|
|
Main entry point for AI Shell
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
from typing import List, Optional
|
|
|
|
from .agent import create_agent
|
|
from . import __version__
|
|
|
|
def execute_command(command: str) -> None:
|
|
"""Execute a shell command"""
|
|
os.system(command)
|
|
|
|
def get_user_confirmation(command: str) -> bool:
|
|
"""Get user confirmation before executing command"""
|
|
print(f"(AI Answer): {command}")
|
|
response = input("Execute? [Y/n]: ").strip().lower()
|
|
return response in ["", "y", "yes"]
|
|
|
|
def process_prompt(prompt: str) -> None:
|
|
"""Process user prompt and generate shell command"""
|
|
if not prompt.strip():
|
|
print("Error: No prompt provided")
|
|
sys.exit(1)
|
|
|
|
# Create AI agent
|
|
agent = create_agent()
|
|
|
|
try:
|
|
# Generate response
|
|
resp = agent.run_sync(prompt)
|
|
answer = resp.output
|
|
|
|
if answer.success and answer.cmd is not None:
|
|
if get_user_confirmation(answer.cmd):
|
|
execute_command(answer.cmd)
|
|
else:
|
|
print(f"(AI Answer): {answer.failure}")
|
|
print("Command generation failed")
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
sys.exit(1)
|
|
|
|
def create_parser() -> argparse.ArgumentParser:
|
|
"""Create command line argument parser"""
|
|
parser = argparse.ArgumentParser(
|
|
prog="ai",
|
|
description="AI-powered shell command generator using DeepSeek V3",
|
|
epilog="Examples:\n"
|
|
" ai list files\n"
|
|
" ai \"show disk usage\"\n"
|
|
" ai 显示当前目录\n",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
)
|
|
|
|
parser.add_argument(
|
|
"prompt",
|
|
nargs="*",
|
|
help="Natural language description of the command you want"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--version",
|
|
action="version",
|
|
version=f"ai-shell {__version__}"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--config",
|
|
action="store_true",
|
|
help="Show current configuration"
|
|
)
|
|
|
|
return parser
|
|
|
|
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_missing_modern_commands, get_installation_hint)
|
|
|
|
print("AI Shell Configuration:")
|
|
print(f" Model: {get_model()}")
|
|
print(f" Base URL: {get_base_url()}")
|
|
|
|
try:
|
|
api_key = get_api_key()
|
|
print(f" API Key: {api_key[:8]}...{api_key[-4:]}")
|
|
except ValueError as e:
|
|
print(f" API Key: ❌ {e}")
|
|
|
|
print(f" Timeout: {get_timeout()}s")
|
|
print(f" Max Retries: {get_max_retries()}")
|
|
|
|
print(f"\nConfiguration Status: {'✅ Valid' if validate_config() else '❌ Invalid'}")
|
|
|
|
print("\nConfiguration Sources (in priority order):")
|
|
print(" 1. Environment variables")
|
|
print(" 2. .env file in current directory")
|
|
print(" 3. .env file in package directory")
|
|
print(" 4. ~/.ai-shell/.env file")
|
|
print(" 5. Default values")
|
|
|
|
print("\nEnvironment Variables:")
|
|
print(" AI_SHELL_API_KEY - API key")
|
|
print(" AI_SHELL_BASE_URL - API base URL")
|
|
print(" AI_SHELL_MODEL - Model name")
|
|
print(" AI_SHELL_TIMEOUT - Request timeout (seconds)")
|
|
print(" AI_SHELL_MAX_RETRIES - Maximum retry attempts")
|
|
print(" AI_SHELL_MODERN_COMMANDS - Custom modern command alternatives")
|
|
|
|
# Show modern commands configuration
|
|
modern_commands = get_available_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(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()))}")
|
|
|
|
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"""
|
|
parser = create_parser()
|
|
args = parser.parse_args()
|
|
|
|
if args.config:
|
|
show_config()
|
|
return
|
|
|
|
if not args.prompt:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
# Validate configuration before processing
|
|
from .config import validate_config
|
|
if not validate_config():
|
|
print("❌ Configuration error: API key not configured.")
|
|
print("Please set AI_SHELL_API_KEY in .env file or environment variable.")
|
|
print("Run 'ai --config' to see current configuration.")
|
|
sys.exit(1)
|
|
|
|
# Join all prompt arguments into a single string
|
|
prompt = " ".join(args.prompt)
|
|
process_prompt(prompt)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|