- Add DewToolCommand mixin that auto-derives MCP tool JSON Schema from ArgParser, eliminating the need to define CLI commands and MCP tools separately - Add schemaFromArgParser() to generate JSON Schema from ArgParser options - Add CommandRegistry.mcpTools recursive collector for all DewToolCommand subcommands - Refactor all kanban subcommands to use the mixin; switch get/update/delete from positional rest args to --id / -i option for schema compatibility - Promote list to a proper CLI subcommand (was MCP-only before) - Add search, comment, and config subcommands (CLI + MCP tools) - Add TicketStore.addComment() for non-destructive comment appending - Simplify mcp.registerCommands() to take only CommandRegistry - Simplify CLI entry point (no more KanbanToolProvider/McpToolRegistry) - Delete stale files: kanban_tool_provider.dart, mcp_tool_provider.dart, mcp_tool_registry.dart (superseded by DewToolCommand mixin) - Add tools/mcp_client.dart debug client for manual MCP server testing - Update .vscode/mcp.json with correct server config All 26 tests pass, dart analyze clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
46 lines
1.3 KiB
Dart
46 lines
1.3 KiB
Dart
import 'package:dew_core/dew_core.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import '../ticket_store.dart';
|
|
|
|
class ListCommand extends DewCommand with DewToolCommand {
|
|
ListCommand() {
|
|
argParser
|
|
..addOption('column', abbr: 'c', help: 'Filter to tickets in this column.')
|
|
..addOption('type', abbr: 't', help: 'Filter to tickets of this type.');
|
|
}
|
|
|
|
@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<String> callAsTool(Map<String, dynamic> args) async {
|
|
final columnFilter = args['column'] as String?;
|
|
final typeFilter = args['type'] as String?;
|
|
|
|
final context = await ProjectContext.find();
|
|
final store = TicketStore(
|
|
kanbanDir: p.join(context.root, '.project', 'kanban'),
|
|
prefix: context.config.kanban.prefix,
|
|
);
|
|
var tickets = await store.list();
|
|
|
|
if (columnFilter != null) {
|
|
tickets = tickets.where((t) => t.column == columnFilter).toList();
|
|
}
|
|
if (typeFilter != null) {
|
|
tickets = tickets.where((t) => t.type == typeFilter).toList();
|
|
}
|
|
|
|
if (tickets.isEmpty) return 'No tickets found.';
|
|
return tickets
|
|
.map((t) => '[${t.id}] (${t.type}) [${t.column}] ${t.title}')
|
|
.join('\n');
|
|
}
|
|
}
|