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
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:02 +0200
1import os
2from pathlib import Path
4from rich import print
5from rich.panel import Panel
6from rich.syntax import Syntax
8from ... import T
9from .base import CommandMode, Completable
10from .base_parser import ParserCommand
11from .utils import print_table
14class CustomCommand(ParserCommand):
15 name = 'custom'
16 description = T('Manage custom commands')
17 modes = [CommandMode.MAIN, CommandMode.TASK]
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'))
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'))
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'))
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)
37 def cmd_list(self, args, ctx):
38 """List all custom commands"""
39 custom_commands = self.manager.custom_command_manager.get_all_commands()
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
47 rows = []
48 for cmd in custom_commands:
49 modes_str = ', '.join([mode.value for mode in cmd.modes])
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
62 rows.append([cmd.name, cmd.description, modes_str, file_path_str])
64 print_table(
65 rows,
66 headers=[T('Name'), T('Description'), T('Modes'), T('File')],
67 title=T('Custom Commands')
68 )
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]")
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
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 ))
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]")
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
105 if not command_dirs:
106 print(f"[red]{T('No custom command directories configured')}[/red]")
107 return
109 # Use the first directory for creation
110 command_dir = next(iter(command_dirs))
112 # Ensure the directory exists
113 command_dir.mkdir(parents=True, exist_ok=True)
115 command_file = command_dir / f"{args.name}.md"
117 if command_file.exists():
118 print(f"[red]{T('Command already exists')}: {args.name}[/red]")
119 return
121 # Determine modes
122 if args.mode == 'both':
123 modes = ['main', 'task']
124 else:
125 modes = [args.mode]
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---
139# {args.name.title()} Command
141This is a custom command: {{{{input}}}}
143You can modify this template to create your custom command logic.
145## Available template variables:
146- {{{{input}}}}: The input parameter
147- {{{{subcommand}}}}: The subcommand if any
149## Template syntax:
150- Use Jinja2 syntax for advanced templating
151- Simple {{{{variable}}}} replacement for basic usage
152'''
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]")
161 def cmd(self, args, ctx):
162 """Default action: list commands"""
163 self.cmd_list(args, ctx)