tix-tracker 0.12.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
#[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>,
    },
}