dew/packages/kanban/lib/src/commands/update_command.dart
Chris Hendrickson 0ad1fae213 chore: 1.0 release prep
Metadata:
- Bump dew CLI version 0.0.1 → 1.0.0
- Add repository + issue_tracker URLs to all pubspec.yaml files
- Switch inter-package path deps to versioned deps (^1.0.0)
- Remove publish_to: none from all packages
- Add MIT LICENSE to root and all packages
- Confirm all four pub.dev names available (dew, dew_core, dew_kanban, dew_mcp)

Documentation:
- Add CHANGELOG.md (Keep a Changelog format, full 1.0.0 feature history)
- Overhaul README.md (pitch, pub.dev badge, quick-start, feature sections)
- Add TUI section + full keybinding tables to docs/features/kanban.md
- Add CONTRIBUTING.md (setup, test, lint, branch strategy, command guide)

Tests:
- Add packages/cli/test/cli_test.dart (6 smoke tests)
- Add packages/kanban/test/integration_test.dart (6 TicketStore e2e tests)
- Expand packages/mcp/test/mcp_test.dart (5 tool registration tests)
- Add dew_kanban as dev dependency in packages/mcp/pubspec.yaml
- 57/57 tests passing

Code quality:
- dart format applied across all 23 changed source files
- dart analyze: zero errors, zero warnings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 15:58:54 -04:00

97 lines
3 KiB
Dart

import 'package:dew_core/dew_core.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import '../kanban_config.dart';
import '../ticket_store.dart';
class UpdateCommand extends DewCommand with DewToolCommand {
final FileSystem _fs;
UpdateCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs {
argParser
..addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID.')
..addOption('title', abbr: 't', help: 'New title.')
..addOption('type', help: 'New ticket type.')
..addOption('column', abbr: 'c', help: 'New column.')
..addOption('body', abbr: 'b', help: 'New body (replaces existing body).')
..addMultiOption(
'milestone',
help: 'Replace milestone list (repeatable; omit to leave unchanged).',
)
..addMultiOption(
'label',
help: 'Replace label list (repeatable; omit to leave unchanged).',
);
}
@override
final String name = 'update';
@override
final String description =
'Update one or more fields on an existing kanban ticket.';
@override
final String toolName = 'kanban_update_ticket';
@override
Future<String> callAsTool(Map<String, dynamic> args) async {
final id = (args['id'] as String).toUpperCase();
final title = args['title'] as String?;
final typeId = args['type'] as String?;
final column = args['column'] as String?;
final body = args['body'] as String?;
final rawMilestones = args['milestone'] as List?;
final milestones = rawMilestones != null && rawMilestones.isNotEmpty
? rawMilestones.cast<String>().where((s) => s.isNotEmpty).toList()
: null;
final rawLabels = args['label'] as List?;
final labels = rawLabels != null && rawLabels.isNotEmpty
? rawLabels.cast<String>().where((s) => s.isNotEmpty).toList()
: null;
if (title == null &&
typeId == null &&
column == null &&
body == null &&
milestones == null &&
labels == null) {
throw ArgumentError(
'At least one of --title, --type, --column, --body, --milestone, --label must be specified.',
);
}
final context = await ProjectContext.find(fs: _fs);
final config = context.config.kanban;
if (typeId != null && !config.ticketTypes.any((t) => t.id == typeId)) {
throw ArgumentError(
'Unknown type "$typeId". '
'Valid: ${config.ticketTypes.map((t) => t.id).join(', ')}',
);
}
if (column != null && !config.columns.any((c) => c.id == column)) {
throw ArgumentError(
'Unknown column "$column". '
'Valid: ${config.columns.map((c) => c.id).join(', ')}',
);
}
final store = TicketStore(
kanbanDir: context.dirs.kanban,
prefix: config.prefix,
fs: context.fs,
);
final ticket = await store.update(
id,
title: title,
type: typeId,
column: column,
body: body,
milestones: milestones,
labels: labels,
);
return 'Updated ${ticket.id}.';
}
}