Skip to content

Commit 270ec3d

Browse files
dpwanjalaclaude
andcommitted
feat: add pagination, saved views, and bulk operations
Pagination: - API endpoints return PaginatedResponse with total/page/per_page/has_more - Configurable per_page (default 50, max 200) - Backward compatible — omit page param for existing behavior Saved Views: - `cmt view save <name> --status active --priority high` saves filter sets - `cmt view list` and `cmt view show <name>` to manage/execute views - `cmt view delete <name>` to remove views - `--view <name>` flag on `cmt list` to apply saved filters - Views stored as YAML in `.cmt/views/` - API: GET/POST/DELETE /api/views, GET /api/views/{name}/items Bulk Operations: - `cmt bulk status <target> --filter "status=active"` batch status changes - `cmt bulk done --filter "assignee=agent-1"` batch done - `cmt bulk edit --filter "tag=sprint-3" --set priority=high` batch edits - `cmt bulk delete --filter "status=cancelled" --force` batch delete - `cmt done CMT-1..CMT-5` range syntax for done command - Per-item failure handling (partial success supported) - API: POST /api/bulk/status, /api/bulk/done Total: 227 tests passing (was 207) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2a5dacb commit 270ec3d

12 files changed

Lines changed: 1843 additions & 15 deletions

File tree

crates/cmt-cli/src/cli.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ pub enum Commands {
101101

102102
/// Add or list comments on a work item
103103
Comment(CommentArgs),
104+
105+
/// Manage saved views (named filter sets)
106+
View(ViewArgs),
107+
108+
/// Bulk operations on multiple work items
109+
Bulk(BulkArgs),
104110
}
105111

106112
#[derive(Args, Debug)]
@@ -333,6 +339,10 @@ pub struct ListArgs {
333339
/// Maximum items to show
334340
#[arg(long, short = 'l')]
335341
pub limit: Option<u32>,
342+
343+
/// Apply a saved view (load filters from .cmt/views/<name>.yml)
344+
#[arg(long)]
345+
pub view: Option<String>,
336346
}
337347

338348
#[derive(Args, Debug)]
@@ -562,6 +572,133 @@ pub struct CommentArgs {
562572
pub reply_to: Option<String>,
563573
}
564574

575+
#[derive(Args, Debug)]
576+
pub struct ViewArgs {
577+
#[command(subcommand)]
578+
pub command: ViewCommand,
579+
}
580+
581+
#[derive(Subcommand, Debug)]
582+
pub enum ViewCommand {
583+
/// List saved views
584+
List,
585+
/// Save a named view
586+
Save(SaveViewArgs),
587+
/// Execute a saved view (show matching items)
588+
Show {
589+
/// View name
590+
name: String,
591+
},
592+
/// Delete a saved view
593+
Delete {
594+
/// View name
595+
name: String,
596+
},
597+
}
598+
599+
#[derive(Args, Debug)]
600+
pub struct SaveViewArgs {
601+
/// View name
602+
pub name: String,
603+
604+
/// Description of this view
605+
#[arg(long, short = 'd')]
606+
pub description: Option<String>,
607+
608+
/// Filter by status
609+
#[arg(long, short = 's')]
610+
pub status: Option<String>,
611+
612+
/// Filter by type
613+
#[arg(long, short = 't')]
614+
pub r#type: Option<String>,
615+
616+
/// Filter by priority
617+
#[arg(long, short = 'p')]
618+
pub priority: Option<String>,
619+
620+
/// Filter by assignee
621+
#[arg(long, short = 'a')]
622+
pub assignee: Option<String>,
623+
624+
/// Filter by tag
625+
#[arg(long)]
626+
pub tag: Option<String>,
627+
628+
/// Sort field
629+
#[arg(long)]
630+
pub sort: Option<String>,
631+
632+
/// Maximum items
633+
#[arg(long, short = 'l')]
634+
pub limit: Option<u32>,
635+
}
636+
637+
#[derive(Args, Debug)]
638+
pub struct BulkArgs {
639+
#[command(subcommand)]
640+
pub command: BulkCommand,
641+
}
642+
643+
#[derive(Subcommand, Debug)]
644+
pub enum BulkCommand {
645+
/// Change status of matching items
646+
Status(BulkStatusArgs),
647+
/// Mark matching items as done
648+
Done(BulkDoneArgs),
649+
/// Edit fields on matching items
650+
Edit(BulkEditArgs),
651+
/// Delete matching items
652+
Delete(BulkDeleteArgs),
653+
}
654+
655+
#[derive(Args, Debug)]
656+
pub struct BulkStatusArgs {
657+
/// Target status
658+
pub target: String,
659+
660+
/// Filter expression (e.g., "status=active,priority=high")
661+
#[arg(long, short)]
662+
pub filter: String,
663+
664+
/// Force transition even if not allowed
665+
#[arg(long)]
666+
pub force: bool,
667+
}
668+
669+
#[derive(Args, Debug)]
670+
pub struct BulkDoneArgs {
671+
/// Filter expression (e.g., "status=active,assignee=agent-1")
672+
#[arg(long, short)]
673+
pub filter: String,
674+
675+
/// Force transition even if not allowed
676+
#[arg(long)]
677+
pub force: bool,
678+
}
679+
680+
#[derive(Args, Debug)]
681+
pub struct BulkEditArgs {
682+
/// Filter expression (e.g., "tag=sprint-3")
683+
#[arg(long, short)]
684+
pub filter: String,
685+
686+
/// Set field value (repeatable, key=value)
687+
#[arg(long, action = clap::ArgAction::Append)]
688+
pub set: Vec<String>,
689+
}
690+
691+
#[derive(Args, Debug)]
692+
pub struct BulkDeleteArgs {
693+
/// Filter expression (e.g., "status=cancelled")
694+
#[arg(long, short)]
695+
pub filter: String,
696+
697+
/// Force deletion (required for bulk delete)
698+
#[arg(long)]
699+
pub force: bool,
700+
}
701+
565702
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
566703
pub enum PriorityValue {
567704
Critical,

0 commit comments

Comments
 (0)