#!/usr/bin/env python3
"""
CLI for managing templates and cheatsheets.
Provides commands to:
- List available templates and cheatsheets
- View/display content in terminal
- Copy templates to user projects
- Validate template syntax
"""
import shutil
from pathlib import Path
from typing import Optional, Literal
from super_pocket.settings import click, CONTEXT_SETTINGS, add_help_command
from rich.console import Console
from rich.markdown import Markdown
from rich.table import Table
from . import TEMPLATES_DIR, CHEATSHEETS_DIR
console = Console()
[docs]
def get_available_items(item_type: Literal["templates", "cheatsheets"]) -> list[Path]:
"""
Get list of available templates or cheatsheets.
Scans the appropriate directory and returns all Markdown files (.md) found,
sorted alphabetically by filename.
Args:
item_type: Either "templates" or "cheatsheets" to specify which directory to scan.
Returns:
list[Path]: Sorted list of Path objects for available Markdown items.
Example:
>>> templates = get_available_items("templates")
>>> [t.name for t in templates]
['agent_template.md', 'unit_tests_agent.md']
"""
directory = TEMPLATES_DIR if item_type == "templates" else CHEATSHEETS_DIR
return sorted(directory.glob("*.md"))
@click.group(context_settings=CONTEXT_SETTINGS)
def documents_cli():
"""
Manage agent templates and development cheatsheets.
Provides commands to list, view, and copy templates/cheatsheets
to your projects.
"""
pass
@documents_cli.command(name="list", context_settings=CONTEXT_SETTINGS)
@click.option(
'--type', '-t',
type=click.Choice(['templates', 'cheatsheets', 'all'], case_sensitive=False),
default='all',
help='Type of items to list.'
)
def list_items(type: str):
"""
List available templates and cheatsheets.
Displays a formatted table showing all available templates and/or cheatsheets
with their names and descriptions (extracted from the first line of each file).
Args:
type: Filter by type - 'templates', 'cheatsheets', or 'all' (default: all).
Examples:
pocket documents list
pocket documents list --type templates
pocket documents list -t cheatsheets
"""
table = Table(title="Available Templates & Cheatsheets", show_header=True, header_style="bold magenta")
table.add_column("Type", style="cyan", width=15)
table.add_column("Name", style="green")
table.add_column("Description", style="white")
if type in ['templates', 'all']:
templates = get_available_items("templates")
for template in templates:
# Extract description from first line if available
try:
first_line = template.read_text(encoding='utf-8').split('\n')[0]
description = first_line.replace('#', '').strip()[:60]
except Exception:
description = "N/A"
table.add_row("Template", template.stem, description)
if type in ['cheatsheets', 'all']:
cheatsheets = get_available_items("cheatsheets")
for cheatsheet in cheatsheets:
try:
first_line = cheatsheet.read_text(encoding='utf-8').split('\n')[0]
description = first_line.replace('#', '').strip()[:60]
except Exception:
description = "N/A"
table.add_row("Cheatsheet", cheatsheet.stem, description)
console.print(table)
@documents_cli.command(name="view", context_settings=CONTEXT_SETTINGS)
@click.argument('name', type=str)
@click.option(
'--type', '-t',
type=click.Choice(['template', 'cheatsheet'], case_sensitive=False),
help='Type of item to view (auto-detected if not specified).'
)
def view_item(name: str, type: Optional[str]):
"""
View/display a template or cheatsheet in the terminal.
Renders the template or cheatsheet content with Markdown formatting
directly in the terminal using Rich. If type is not specified, the
function will attempt to auto-detect by searching templates first,
then cheatsheets.
Args:
name: Name of the template or cheatsheet (without .md extension).
type: Optional type specification - 'template' or 'cheatsheet'.
Auto-detected if not provided.
Raises:
click.Abort: If the item is not found or cannot be read.
Examples:
pocket documents view unit_tests_agent
pocket documents view SQL -t cheatsheet
"""
# Try to find the item
item_path = None
if type == 'template' or type is None:
potential_path = TEMPLATES_DIR / f"{name}.md"
if potential_path.exists():
item_path = potential_path
if type == 'cheatsheet' or (type is None and item_path is None):
potential_path = CHEATSHEETS_DIR / f"{name}.md"
if potential_path.exists():
item_path = potential_path
if item_path is None:
console.print(f"[red]Error:[/red] Item '{name}' not found.", style="bold")
console.print("\nUse 'pocket documents list' to see available items.")
raise click.Abort()
try:
content = item_path.read_text(encoding='utf-8')
md = Markdown(content)
console.print(md)
except Exception as e:
console.print(f"[red]Error reading file:[/red] {e}", style="bold")
raise click.Abort()
@documents_cli.command(name="copy", context_settings=CONTEXT_SETTINGS)
@click.argument('name', type=str)
@click.option(
'--output', '-o',
type=click.Path(path_type=Path),
help='Output path for the copied file. If directory, file will be copied there with original name.'
)
@click.option(
'--type', '-t',
type=click.Choice(['template', 'cheatsheet'], case_sensitive=False),
help='Type of item to copy (auto-detected if not specified).'
)
@click.option(
'--force', '-f',
is_flag=True,
help='Overwrite existing file if it exists.'
)
def copy_item(name: str, output: Optional[Path], type: Optional[str], force: bool):
"""
Copy a template or cheatsheet to your project.
Copies the specified template or cheatsheet file to a destination in your
project. Creates parent directories if they don't exist. If the output path
is a directory, the file is copied there with its original name.
Args:
name: Name of the template or cheatsheet (without .md extension).
output: Output path (file or directory) for the copied file.
Defaults to current directory if not specified.
type: Optional type specification - 'template' or 'cheatsheet'.
Auto-detected if not provided.
force: If True, overwrite existing file without confirmation.
Raises:
click.Abort: If the item is not found or copy operation fails.
Examples:
pocket documents copy unit_tests_agent -o .agents/
pocket documents copy SQL -o docs/cheatsheets/ -t cheatsheet
pocket documents copy my_agent -o ./custom.md -f
"""
# Find the source item
source_path = None
if type == 'template' or type is None:
potential_path = TEMPLATES_DIR / f"{name}.md"
if potential_path.exists():
source_path = potential_path
if type == 'cheatsheet' or (type is None and source_path is None):
potential_path = CHEATSHEETS_DIR / f"{name}.md"
if potential_path.exists():
source_path = potential_path
if source_path is None:
console.print(f"[red]Error:[/red] Item '{name}' not found.", style="bold")
console.print("\nUse 'pocket documents list' to see available items.")
raise click.Abort()
# Determine output path
if output is None:
output = Path.cwd() / source_path.name
elif output.is_dir():
output = output / source_path.name
# Check if file exists
if output.exists() and not force:
console.print(f"[yellow]Warning:[/yellow] File '{output}' already exists.", style="bold")
if not click.confirm("Overwrite?"):
console.print("Operation cancelled.")
return
try:
# Create parent directories if needed
output.parent.mkdir(parents=True, exist_ok=True)
# Copy the file
shutil.copy2(source_path, output)
console.print(f"[green]✓[/green] Successfully copied '{source_path.name}' to '{output}'", style="bold")
except Exception as e:
console.print(f"[red]Error copying file:[/red] {e}", style="bold")
raise click.Abort()
@documents_cli.command(name="init", context_settings=CONTEXT_SETTINGS)
@click.option(
'--output', '-o',
type=click.Path(path_type=Path),
default=Path.cwd() / ".AGENTS",
help='Directory where agent templates will be initialized.'
)
def init_agents(output: Path):
"""
Initialize agent configuration directory with all templates.
Creates a directory and copies all available agent templates into it.
This is useful for quickly setting up agent configurations in a new project.
The directory is created if it doesn't exist.
Args:
output: Directory where agent templates will be copied.
Defaults to '.AGENTS' in the current working directory.
Raises:
click.Abort: If an error occurs during directory creation or file copying.
Examples:
pocket documents init
pocket documents init -o ./agents/
pocket documents init -o /path/to/my-project/.agents
"""
try:
# Create output directory
output.mkdir(parents=True, exist_ok=True)
templates = get_available_items("templates")
if not templates:
console.print("[yellow]Warning:[/yellow] No templates found to copy.", style="bold")
return
console.print(f"Initializing agent templates in: [cyan]{output}[/cyan]")
copied_count = 0
for template in templates:
dest = output / template.name
shutil.copy2(template, dest)
console.print(f" [green]✓[/green] {template.name}")
copied_count += 1
console.print(f"\n[green]Success![/green] Copied {copied_count} template(s) to '{output}'", style="bold")
except Exception as e:
console.print(f"[red]Error:[/red] {e}", style="bold")
raise click.Abort()
# Add 'help' subcommand to the group
add_help_command(documents_cli)
if __name__ == '__main__':
documents_cli()