issue-core/issue_core/cli/main.py

136 lines
4.4 KiB
Python
Raw Permalink Normal View History

2025-10-25 00:54:20 +02:00
"""
Main CLI Entry Point
Universal Issue Tracking System CLI
"""
import click
import sys
from pathlib import Path
from .commands import issue_group
from .backend_commands import backend_group
from .sync_commands import sync_group
from .serve_command import serve_command
from .map_commands import map_group, project_command
from .. import __version__
2025-10-25 00:54:20 +02:00
@click.group()
@click.version_option(version=__version__, package_name='issue-core')
2025-10-25 00:54:20 +02:00
@click.option('--config', type=click.Path(), help='Configuration file path')
@click.option('--backend', help='Backend to use (local, gitea)')
@click.option('--verbose', '-v', is_flag=True, help='Verbose output')
@click.pass_context
def cli(ctx, config, backend, verbose):
"""
External issue-tracker connector (Gitea/Forgejo, local SQLite).
2025-10-25 00:54:20 +02:00
Not the origin of fleet work records see INTENT.md. Use project/map
to link work-record UUIDs to external issues when a tracker is in use.
2025-10-25 00:54:20 +02:00
Examples:
issue list
issue create "Tracker note"
issue project <work-record-uuid> --title "..."
issue map show --uuid <uuid>
issue map push-status --uuid <uuid> --status progress
backend add local ~/.issues
sync pull gitea
2025-10-25 00:54:20 +02:00
"""
# Ensure the object exists
ctx.ensure_object(dict)
# Store global options in context
ctx.obj['config_path'] = config
ctx.obj['backend'] = backend
ctx.obj['verbose'] = verbose
# Register command groups
cli.add_command(issue_group, name='issue')
cli.add_command(backend_group, name='backend')
cli.add_command(sync_group, name='sync')
cli.add_command(serve_command)
cli.add_command(map_group, name='map')
cli.add_command(project_command, name='project')
2025-10-25 00:54:20 +02:00
# Convenience aliases - direct issue commands
@cli.command('list')
@click.option('--state', type=click.Choice(['open', 'closed', 'all']), default='open', help='Issue state filter')
@click.option('--assignee', help='Filter by assignee')
@click.option('--label', multiple=True, help='Filter by labels')
@click.option('--milestone', help='Filter by milestone')
@click.option('--search', help='Search in title and description')
@click.option('--limit', type=int, default=30, help='Maximum number of issues to show')
@click.option('--format', 'output_format', type=click.Choice(['table', 'json', 'compact']), default='table', help='Output format')
2025-10-25 00:54:20 +02:00
@click.pass_context
def list_issues(ctx, state, assignee, label, milestone, search, limit, output_format):
2025-10-25 00:54:20 +02:00
"""List all issues (alias for 'issue list')."""
ctx.invoke(
issue_group.get_command(ctx, 'list'),
state=state,
assignee=assignee,
label=label,
milestone=milestone,
search=search,
limit=limit,
output_format=output_format
)
2025-10-25 00:54:20 +02:00
@cli.command('show')
@click.argument('issue_number', type=int)
@click.pass_context
def show_issue(ctx, issue_number):
"""Show issue details (alias for 'issue show')."""
ctx.invoke(issue_group.get_command(ctx, 'show'), issue_number=issue_number)
@cli.command('create')
@click.argument('title')
@click.option('--description', '-d', help='Issue description')
@click.option('--label', '-l', multiple=True, help='Labels to add')
@click.option('--assignee', '-a', help='Assign to user')
@click.option('--milestone', '-m', help='Milestone')
@click.pass_context
def create_issue(ctx, title, description, label, assignee, milestone):
"""Create new issue (alias for 'issue create')."""
ctx.invoke(
issue_group.get_command(ctx, 'create'),
title=title,
description=description,
label=label,
assignee=assignee,
milestone=milestone
)
@cli.command('close')
@click.argument('issue_number', type=int)
@click.option('--comment', '-c', help='Closing comment')
@click.pass_context
def close_issue(ctx, issue_number, comment):
"""Close issue (alias for 'issue close')."""
ctx.invoke(
issue_group.get_command(ctx, 'close'),
issue_number=issue_number,
comment=comment
)
def main():
"""Main entry point for the CLI."""
try:
cli(obj={})
except KeyboardInterrupt:
click.echo("\nAborted by user", err=True)
sys.exit(1)
except Exception as e:
click.echo(f"Error: {e}", err=True)
sys.exit(1)
if __name__ == '__main__':
main()