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