tix/cli/cli_epic.rs
1#[allow(clippy::wildcard_imports)]
2use super::*;
3
4/// The `tix epic` subcommands (mission `epics-work-items-01M3BYW4`, WP05).
5///
6/// A **required** subcommand, like `tix project`. Selection is by the stable id
7/// (the registry key); a name resolves to the same epic as a convenience. A work
8/// item is an ordinary issue (`tix create --epics`, `tix edit --add-epic`), so
9/// there is no task verb here.
10#[derive(Subcommand)]
11pub enum EpicCommand {
12 /// Create an epic and allocate a stable id
13 ///
14 /// The name is a display label, not the identity: `tix epic edit --name`
15 /// changes it and every issue reference survives, because issues reference the id.
16 Create {
17 /// Display name (unique within the registry, case-insensitively)
18 #[arg(value_name = "NAME")]
19 name: String,
20 /// Long-form context
21 #[arg(long)]
22 description: Option<String>,
23 /// Free-form epic status (not a board column, not a category)
24 #[arg(long)]
25 status: Option<String>,
26 /// Owner identity string
27 #[arg(long)]
28 owner: Option<String>,
29 /// Start date (`YYYY-MM-DD`)
30 #[arg(long = "start-date")]
31 start_date: Option<String>,
32 /// Due date (`YYYY-MM-DD`); must be `>= start-date`
33 #[arg(long = "due-date")]
34 due_date: Option<String>,
35 /// Positive relative size, used by the epic rollup
36 ///
37 /// `allow_hyphen_values` so `--weight -5` reaches the service's
38 /// `invalid_weight` refusal rather than being swallowed by clap as an
39 /// unknown flag — the same invalid input must report one thing whichever
40 /// spelling is used.
41 #[arg(long, allow_hyphen_values = true)]
42 weight: Option<String>,
43 /// Project ids this epic concerns, comma-separated
44 ///
45 /// Shape-checked only, never resolved: a well-formed id the registry does
46 /// not hold is stored and diagnosed by `tix lint`, so it is repairable.
47 #[arg(long, value_name = "ID")]
48 projects: Option<String>,
49 /// Milestone name; stored verbatim, never resolved
50 #[arg(long)]
51 milestone: Option<String>,
52 /// Cycle name; stored verbatim, never resolved
53 #[arg(long)]
54 cycle: Option<String>,
55 },
56 /// Edit an epic's fields; a rename changes the label only
57 ///
58 /// Nothing is re-keyed, so every issue reference and derived figure stays
59 /// intact. A blank optional value clears the field (`--owner ""`).
60 Edit {
61 /// Epic id (a name is accepted as a convenience)
62 #[arg(value_name = "ID")]
63 reference: String,
64 /// New display name
65 #[arg(long)]
66 name: Option<String>,
67 /// New long-form context (`--description ""` clears)
68 #[arg(long)]
69 description: Option<String>,
70 /// New free-form status (`--status ""` clears)
71 #[arg(long)]
72 status: Option<String>,
73 /// New owner identity string (`--owner ""` clears)
74 #[arg(long)]
75 owner: Option<String>,
76 /// New start date (`--start-date ""` clears)
77 #[arg(long = "start-date")]
78 start_date: Option<String>,
79 /// New due date (`--due-date ""` clears)
80 #[arg(long = "due-date")]
81 due_date: Option<String>,
82 /// New positive relative size (`--weight ""` clears)
83 ///
84 /// `allow_hyphen_values` so `--weight -5` reaches the service's
85 /// `invalid_weight` refusal rather than clap's unknown-flag error.
86 #[arg(long, allow_hyphen_values = true)]
87 weight: Option<String>,
88 /// Set or change the parent epic; `--parent ""` detaches it
89 ///
90 /// Validated by the epic service before any write, so a self-parent or a
91 /// cycle-closing change leaves the registry byte-identical.
92 #[arg(long, value_name = "ID")]
93 parent: Option<String>,
94 /// Replace the whole project list, comma-separated (`--projects ""` clears)
95 ///
96 /// Shape-checked only, never resolved: a dangling but well-formed id is
97 /// stored and diagnosed by `tix lint`.
98 #[arg(long, value_name = "ID")]
99 projects: Option<String>,
100 /// New milestone name (`--milestone ""` clears); stored verbatim
101 #[arg(long)]
102 milestone: Option<String>,
103 /// New cycle name (`--cycle ""` clears); stored verbatim
104 #[arg(long)]
105 cycle: Option<String>,
106 },
107 /// List epics in declaration order, with ids, member and child counts
108 List,
109 /// Show one epic's record, hierarchy, progress and members
110 Show {
111 /// Epic id (a name is accepted as a convenience)
112 #[arg(value_name = "ID")]
113 reference: String,
114 },
115 /// Navigate an epic's hierarchy: parent, children, ancestors and depth
116 ///
117 /// Direct issue navigation stays available: the member list is included and a
118 /// member is an ordinary issue, reachable with `tix show <number>`.
119 Navigate {
120 /// Epic id (a name is accepted as a convenience)
121 #[arg(value_name = "ID")]
122 reference: String,
123 },
124 /// Delete an epic's registry entry (its issues and child epics are never deleted)
125 ///
126 /// The issues that referenced the epic keep their references and the child
127 /// epics keep their parent reference; both are reported so the dangling
128 /// references can be repaired deliberately.
129 Delete {
130 /// Epic id (a name is accepted as a convenience)
131 #[arg(value_name = "ID")]
132 reference: String,
133 /// Confirm without prompting
134 #[arg(long)]
135 yes: bool,
136 },
137 /// Reference an epic from one or more issues
138 ///
139 /// Each selector is processed independently: a rejection on one is reported
140 /// and does not abort the rest.
141 Add {
142 /// Epic id (a name is accepted as a convenience)
143 #[arg(value_name = "ID")]
144 reference: String,
145 /// Issue numbers, filename fragments or unique title fragments
146 #[arg(required = true, value_name = "ISSUE")]
147 issues: Vec<String>,
148 },
149 /// Remove an epic reference from one or more issues
150 ///
151 /// Drops only the reference; no issue is deleted. A reference that does not
152 /// name a live epic is used **verbatim**, so a retained stale id can still be
153 /// removed and is reported as `0 changed` rather than refused.
154 Remove {
155 /// Epic id (a name is accepted as a convenience)
156 #[arg(value_name = "ID")]
157 reference: String,
158 /// Issue numbers, filename fragments or unique title fragments
159 #[arg(required = true, value_name = "ISSUE")]
160 issues: Vec<String>,
161 },
162 /// Bulk-reference one or more issues from an epic
163 ///
164 /// Epic reference only — general bulk editing is a different feature. Every
165 /// selector is independent, so one unknown selector does not discard the rest.
166 /// An unknown **epic** reference refuses the whole command before anything is
167 /// written; use `tix epic remove` to drop a retained stale id.
168 BulkAdd {
169 /// Epic id (a name is accepted as a convenience)
170 #[arg(value_name = "ID")]
171 reference: String,
172 /// Issue numbers, filename fragments or unique title fragments
173 #[arg(required = true, value_name = "ISSUE")]
174 issues: Vec<String>,
175 },
176}