import 'package:dew_core/dew_core.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; import '../kanban_config.dart'; import '../ticket.dart'; import '../ticket_store.dart'; class ListCommand extends DewCommand with DewToolCommand { final FileSystem _fs; ListCommand({this._fs = const LocalFileSystem()}) { argParser ..addOption( 'column', abbr: 'c', help: 'Filter to tickets in this column.', ) ..addOption('type', abbr: 't', help: 'Filter to tickets of this type.') ..addOption('label', help: 'Filter to tickets with this label.') ..addOption('milestone', help: 'Filter to tickets in this milestone.') ..addFlag( 'include-archived', help: 'Include archived tickets.', negatable: false, ); } @override final String name = 'list'; @override final String description = 'List kanban tickets, optionally filtered by column or type.'; @override final String toolName = 'kanban_list_tickets'; @override Future callAsTool(Map args) async { final columnFilter = args['column'] != null ? '${args['column']}' : null; final typeFilter = args['type'] != null ? '${args['type']}' : null; final labelFilter = args['label'] != null ? '${args['label']}' : null; final milestoneFilter = args['milestone'] != null ? '${args['milestone']}' : null; final includeArchived = args['include-archived'] as bool? ?? false; final context = await ProjectContext.find(fs: _fs); final store = TicketStore( kanbanDir: context.dirs.kanban, prefix: context.config.kanban.prefix, fs: context.fs, ); var tickets = await store.list(includeArchived: includeArchived); if (columnFilter != null) { tickets = tickets.where((t) => t.column == columnFilter).toList(); } if (typeFilter != null) { tickets = tickets.where((t) => t.type == typeFilter).toList(); } if (labelFilter != null) { tickets = tickets.where((t) => t.labels.contains(labelFilter)).toList(); } if (milestoneFilter != null) { tickets = tickets .where((t) => t.milestones.contains(milestoneFilter)) .toList(); } if (tickets.isEmpty) return 'No tickets found.'; // If a column filter is applied, skip the grouping header. if (columnFilter != null) { return tickets.map((t) => '[${t.id}] (${t.type}) ${t.title}').join('\n'); } // Group by column and emit with headers. final byColumn = >{}; for (final t in tickets) { (byColumn[t.column] ??= []).add(t); } final buf = StringBuffer(); var first = true; for (final column in byColumn.keys) { if (!first) buf.writeln(); first = false; final count = byColumn[column]!.length; buf.writeln('$column ($count)'); for (final t in byColumn[column]!) { buf.writeln(' [${t.id}] (${t.type}) ${t.title}'); } } return buf.toString().trimRight(); } }