Coverage for aipyapp/cli/command/cmd_custom.py: 0%

85 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-11 12:02 +0200

1import os 

2from pathlib import Path 

3 

4from rich import print 

5from rich.panel import Panel 

6from rich.syntax import Syntax 

7 

8from ... import T 

9from .base import CommandMode, Completable 

10from .base_parser import ParserCommand 

11from .utils import print_table 

12 

13 

14class CustomCommand(ParserCommand): 

15 name = 'custom' 

16 description = T('Manage custom commands') 

17 modes = [CommandMode.MAIN, CommandMode.TASK] 

18 

19 def add_subcommands(self, subparsers): 

20 subparsers.add_parser('list', help=T('List all custom commands')) 

21 subparsers.add_parser('reload', help=T('Reload custom commands from disk')) 

22 

23 show_parser = subparsers.add_parser('show', help=T('Show custom command details')) 

24 show_parser.add_argument('name', type=str, help=T('Custom command name')) 

25 

26 create_parser = subparsers.add_parser('create', help=T('Create new custom command')) 

27 create_parser.add_argument('name', type=str, help=T('Command name')) 

28 create_parser.add_argument('--description', type=str, default='', help=T('Command description')) 

29 create_parser.add_argument('--mode', choices=['main', 'task', 'both'], default='task', help=T('Command mode')) 

30 

31 def get_arg_values(self, arg, subcommand=None, partial_value=''): 

32 if arg.name == 'name' and subcommand == 'show': 

33 custom_commands = self.manager.custom_command_manager.get_all_commands() 

34 return [Completable(cmd.name, cmd.description) for cmd in custom_commands] 

35 return super().get_arg_values(arg, subcommand, partial_value) 

36 

37 def cmd_list(self, args, ctx): 

38 """List all custom commands""" 

39 custom_commands = self.manager.custom_command_manager.get_all_commands() 

40 

41 if not custom_commands: 

42 print(f"[yellow]{T('No custom commands found')}[/yellow]") 

43 dirs_str = ', '.join(str(d) for d in self.manager.custom_command_manager.command_dirs) 

44 print(f"{T('Custom commands directories')}: {dirs_str}") 

45 return 

46 

47 rows = [] 

48 for cmd in custom_commands: 

49 modes_str = ', '.join([mode.value for mode in cmd.modes]) 

50 

51 # Find which directory this command belongs to 

52 file_path_str = str(cmd.file_path) 

53 for command_dir in self.manager.custom_command_manager.command_dirs: 

54 try: 

55 relative_path = cmd.file_path.relative_to(command_dir) 

56 file_path_str = str(relative_path) 

57 break 

58 except ValueError: 

59 # This file is not in this directory, try next 

60 continue 

61 

62 rows.append([cmd.name, cmd.description, modes_str, file_path_str]) 

63 

64 print_table( 

65 rows, 

66 headers=[T('Name'), T('Description'), T('Modes'), T('File')], 

67 title=T('Custom Commands') 

68 ) 

69 

70 def cmd_reload(self, args, ctx): 

71 """Reload custom commands from disk""" 

72 count = self.manager.reload_custom_commands() 

73 print(f"[green]{T('Reloaded')} {count} {T('custom commands')}[/green]") 

74 

75 def cmd_show(self, args, ctx): 

76 """Show custom command details""" 

77 command = self.manager.custom_command_manager.get_command(args.name) 

78 if not command: 

79 print(f"[red]{T('Custom command not found')}: {args.name}[/red]") 

80 return 

81 

82 # Show command info 

83 print(Panel( 

84 f"**{T('Name')}:** {command.name}\n" 

85 f"**{T('Description')}:** {command.description}\n" 

86 f"**{T('Modes')}:** {', '.join([mode.value for mode in command.modes])}\n" 

87 f"**{T('File')}:** {command.file_path}", 

88 title=T('Command Details'), 

89 border_style="blue" 

90 )) 

91 

92 # Show command content 

93 try: 

94 content = command.file_path.read_text(encoding='utf-8') 

95 syntax = Syntax(content, "markdown", theme="monokai", line_numbers=True) 

96 print(Panel(syntax, title=T('Command Content'), border_style="green")) 

97 except Exception as e: 

98 print(f"[red]{T('Error reading file')}: {e}[/red]") 

99 

100 def cmd_create(self, args, ctx): 

101 """Create a new custom command""" 

102 # Use the first available directory or create a default one 

103 command_dirs = self.manager.custom_command_manager.command_dirs 

104 

105 if not command_dirs: 

106 print(f"[red]{T('No custom command directories configured')}[/red]") 

107 return 

108 

109 # Use the first directory for creation 

110 command_dir = next(iter(command_dirs)) 

111 

112 # Ensure the directory exists 

113 command_dir.mkdir(parents=True, exist_ok=True) 

114 

115 command_file = command_dir / f"{args.name}.md" 

116 

117 if command_file.exists(): 

118 print(f"[red]{T('Command already exists')}: {args.name}[/red]") 

119 return 

120 

121 # Determine modes 

122 if args.mode == 'both': 

123 modes = ['main', 'task'] 

124 else: 

125 modes = [args.mode] 

126 

127 # Create command template 

128 template_content = f'''--- 

129name: "{args.name}" 

130description: "{args.description or f'Custom {args.name} command'}" 

131modes: {modes} 

132arguments: 

133 - name: "input" 

134 type: "str" 

135 required: false 

136 help: "Input parameter" 

137--- 

138 

139# {args.name.title()} Command 

140 

141This is a custom command: {{{{input}}}} 

142 

143You can modify this template to create your custom command logic. 

144 

145## Available template variables: 

146- {{{{input}}}}: The input parameter 

147- {{{{subcommand}}}}: The subcommand if any 

148 

149## Template syntax: 

150- Use Jinja2 syntax for advanced templating 

151- Simple {{{{variable}}}} replacement for basic usage 

152''' 

153 

154 try: 

155 command_file.write_text(template_content, encoding='utf-8') 

156 print(f"[green]{T('Created custom command')}: {command_file}[/green]") 

157 print(f"[yellow]{T('Run')}: /custom reload {T('to load the new command')}[/yellow]") 

158 except Exception as e: 

159 print(f"[red]{T('Error creating command')}: {e}[/red]") 

160 

161 def cmd(self, args, ctx): 

162 """Default action: list commands""" 

163 self.cmd_list(args, ctx)