Skip to main content

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}