dew/packages/kanban/lib/src/commands/archive_command.dart
Chris Hendrickson 53f9493364 Add ProjectDirs extension pattern and injectable clock
- core: Add ProjectDirs helper class on ProjectContext (context.dirs)
  with project getter for .project/ dir; feature packages extend it
  via extension methods to expose their own dirs

- kanban: Add KanbanDirs extension on ProjectDirs exposing .kanban;
  replace all 14 p.join(context.root, '.project', 'kanban') call
  sites with context.dirs.kanban; drop now-unused path imports

- kanban: Add clock: DateTime Function() field to TicketStore
  (defaults to DateTime.now); use clock().toUtc() in create() for
  deterministic timestamps in tests

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

49 lines
1.7 KiB
Dart

import 'package:dew_core/dew_core.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:path/path.dart' as p;
import '../kanban_config.dart';
import '../ticket_store.dart';
class ArchiveCommand extends DewCommand with DewToolCommand {
final FileSystem _fs;
ArchiveCommand({FileSystem fs = const LocalFileSystem()}) : _fs = fs {
argParser.addOption('id', abbr: 'i', mandatory: true, help: 'Ticket ID to archive.');
}
@override
final String name = 'archive';
@override
final String description = 'Archive a ticket (moves it to the archive column).';
@override
final String toolName = 'kanban_archive_ticket';
@override
Future<String> callAsTool(Map<String, dynamic> args) async {
final id = (args['id'] as String).toUpperCase();
final context = await ProjectContext.find(fs: _fs);
final config = context.config.kanban;
final kanbanDir = context.dirs.kanban;
final store = TicketStore(kanbanDir: kanbanDir, prefix: config.prefix, fs: context.fs);
final ticket = await store.findById(id);
if (ticket == null) throw ArgumentError('Ticket $id not found.');
if (ticket.column == 'archive') return '$id is already archived.';
final archiveDir = context.fs.directory(p.join(kanbanDir, 'archive'));
await archiveDir.create(recursive: true);
// Move the file directly rather than through update() so we don't write
// the column back into the ticket frontmatter (it's derived from the dir).
final srcFile = context.fs.file(p.join(kanbanDir, ticket.column, '$id.md'));
final dstFile = context.fs.file(p.join(archiveDir.path, '$id.md'));
await srcFile.rename(dstFile.path);
return 'Archived $id.';
}
}