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
|
feat: rename to issue-core and add task ingestion endpoint
Renames the package, distribution, CLI alias, Makefile targets, and
working directory from issue-facade to issue-core, signalling its
role as the authoritative task lifecycle manager for the Coulomb org
(peer to activity-core, rules-core, project-core).
Adds POST /issues/ ingestion endpoint for activity-core's IssueSink,
under a new optional [api] extra. The endpoint is served by `issue
serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or
X-API-Key header), and routes the TaskSpec payload to the configured
default backend with full traceability metadata embedded in
sync_metadata.
- T01: Python package issue_tracker -> issue_core, dir rename
- T02: registered in state hub under custodian domain
- T03: INTENT.md (what it is, what it isn't, how it fits)
- T04: SCOPE.md (in/out-of-scope, integration boundaries)
- T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests
- T06: docs/nats-task-ingestion.md design stub
Closes ISSC-WP-0001.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:16:27 +02:00
|
|
|
from .serve_command import serve_command
|
2026-07-22 00:25:06 +02:00
|
|
|
from .map_commands import map_group, project_command
|
2025-11-10 10:48:31 +01:00
|
|
|
from .. import __version__
|
2025-10-25 00:54:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@click.group()
|
feat: rename to issue-core and add task ingestion endpoint
Renames the package, distribution, CLI alias, Makefile targets, and
working directory from issue-facade to issue-core, signalling its
role as the authoritative task lifecycle manager for the Coulomb org
(peer to activity-core, rules-core, project-core).
Adds POST /issues/ ingestion endpoint for activity-core's IssueSink,
under a new optional [api] extra. The endpoint is served by `issue
serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or
X-API-Key header), and routes the TaskSpec payload to the configured
default backend with full traceability metadata embedded in
sync_metadata.
- T01: Python package issue_tracker -> issue_core, dir rename
- T02: registered in state hub under custodian domain
- T03: INTENT.md (what it is, what it isn't, how it fits)
- T04: SCOPE.md (in/out-of-scope, integration boundaries)
- T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests
- T06: docs/nats-task-ingestion.md design stub
Closes ISSC-WP-0001.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:16:27 +02:00
|
|
|
@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):
|
|
|
|
|
"""
|
2026-07-22 00:25:06 +02:00
|
|
|
External issue-tracker connector (Gitea/Forgejo, local SQLite).
|
2025-10-25 00:54:20 +02:00
|
|
|
|
2026-07-22 00:25:06 +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:
|
2026-07-22 00:25:06 +02:00
|
|
|
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')
|
feat: rename to issue-core and add task ingestion endpoint
Renames the package, distribution, CLI alias, Makefile targets, and
working directory from issue-facade to issue-core, signalling its
role as the authoritative task lifecycle manager for the Coulomb org
(peer to activity-core, rules-core, project-core).
Adds POST /issues/ ingestion endpoint for activity-core's IssueSink,
under a new optional [api] extra. The endpoint is served by `issue
serve`, authenticates via the ISSUE_CORE_API_KEY env var (Bearer or
X-API-Key header), and routes the TaskSpec payload to the configured
default backend with full traceability metadata embedded in
sync_metadata.
- T01: Python package issue_tracker -> issue_core, dir rename
- T02: registered in state hub under custodian domain
- T03: INTENT.md (what it is, what it isn't, how it fits)
- T04: SCOPE.md (in/out-of-scope, integration boundaries)
- T05: POST /issues/ via FastAPI + Uvicorn, 9 unit tests
- T06: docs/nats-task-ingestion.md design stub
Closes ISSC-WP-0001.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 05:16:27 +02:00
|
|
|
cli.add_command(serve_command)
|
2026-07-22 00:25:06 +02:00
|
|
|
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')
|
2025-12-17 19:32:37 +01:00
|
|
|
@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
|
2025-12-17 19:32:37 +01:00
|
|
|
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')."""
|
2025-12-17 19:32:37 +01:00
|
|
|
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()
|