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::*;

#[allow(clippy::too_many_lines)]
pub(super) fn descriptors() -> Vec<ToolDescriptor> {
    vec![
        ToolDescriptor {
            name: "list_epics",
            description: "List the epics in the registry, in declaration order, each with its **id**, its display name, its derived member count and its derived child count (mirrors `tix epic list`). The id is always present because selecting by id is the contract and a rename must never look like a new epic (C-003). Never fails: an empty registry is an empty list.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {}
            }),
        },
        ToolDescriptor {
            name: "get_epic",
            description: "Show one epic's record, its raw typed figures, its child epics and its derived, ordered members (mirrors `tix epic show`). Resolved by its **stable id**; a name resolves to the same epic. The figures are the service's typed `EpicRollup` (`done`, `total`, `child_count`, `weighted_progress`, `blocked`, `missing_references`), returned as raw values rather than only as the formatted text, so they compare by value (NFR-003). Read-only: no write, no History entry.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or an epic name that resolves to the same epic." }
                },
                "required": ["epic"]
            }),
        },
        ToolDescriptor {
            name: "create_epic",
            description: "Create an epic (mirrors `tix epic create`). The registry allocates a **stable generated id**, which is returned; the name is a mutable display label and not the identity. Validates the name (non-blank, unpadded, unique case-folded), the dates (`YYYY-MM-DD`, `due_date >= start_date`) and the weight (a positive integer) through the service, so a refusal writes nothing. `projects` is a comma-separated list of project **ids**, shape-checked only — a well-formed id the registry does not hold is stored and diagnosed by `tix lint` (C-005).",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "name": { "type": "string", "description": "Display label. Required and unique in the registry (case-folded); never the identity." },
                    "description": { "type": "string", "description": "Optional long-form context." },
                    "status": { "type": "string", "description": "Optional epic status. Free-form epic metadata — **not** a board column and not a workflow category." },
                    "owner": { "type": "string", "description": "Optional owner identity string (a bare handle or `Name <email>`); not a user account." },
                    "start_date": { "type": "string", "description": "`YYYY-MM-DD`." },
                    "due_date": { "type": "string", "description": "`YYYY-MM-DD`; must be `>= start_date`." },
                    "weight": { "type": ["integer", "string"], "description": "Optional positive relative size, used by the epic rollup and by a parent epic's weighted figure. The CLI spelling is a string; a JSON integer is equivalent, and a string must be a decimal integer (a malformed one is the service's `invalid_weight` refusal)." },
                    "projects": { "type": "string", "description": "Comma-separated project **ids** this epic concerns. Each is shape-checked, never resolved: a dangling but well-formed id is stored and diagnosed by `tix lint` (C-005)." },
                    "milestone": { "type": "string", "description": "Milestone **name** (the existing name-keyed reference), stored verbatim." },
                    "cycle": { "type": "string", "description": "Cycle **name** (the existing name-keyed reference), stored verbatim." }
                },
                "required": ["name"]
            }),
        },
        ToolDescriptor {
            name: "update_epic",
            description: "Edit an epic's fields (mirrors `tix epic edit`). Resolved by its **stable id** (or a name). A `name` change is a **label change only**: the id is untouched, so no reference and no derived figure is re-keyed (C-003, NFR-002). A blank optional field (`\"\"`) clears it. `parent` is validated by the service's iterative epic-chain walk **before** any write, so a self-parent or a cycle-closing change leaves the registry byte-unchanged. `projects` **replaces the whole list** (blank clears it), shape-checked only. At least one field must be provided.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." },
                    "name": { "type": "string", "description": "New display label. The id does not change. A name is never cleared (an empty name is refused as `duplicate_name`)." },
                    "description": { "type": "string", "description": "New description; `\"\"` clears it." },
                    "status": { "type": "string", "description": "New epic status; `\"\"` clears it." },
                    "owner": { "type": "string", "description": "New owner identity string; `\"\"` clears it." },
                    "start_date": { "type": "string", "description": "New `YYYY-MM-DD` start date; `\"\"` clears it." },
                    "due_date": { "type": "string", "description": "New `YYYY-MM-DD` due date; `\"\"` clears it." },
                    "weight": { "type": ["integer", "string"], "description": "New positive relative size; `\"\"` clears it. A malformed string is the service's `invalid_weight` refusal." },
                    "parent": { "type": "string", "description": "Set or change the parent, resolved by **stable id** (a name resolves to the same epic); `\"\"` detaches it. Validated by the epic service's cycle walk before any write." },
                    "projects": { "type": "string", "description": "Replace the whole project-id list, comma-separated; `\"\"` clears it. Shape-checked only, never resolved." },
                    "milestone": { "type": "string", "description": "New milestone name; `\"\"` clears it. Stored verbatim." },
                    "cycle": { "type": "string", "description": "New cycle name; `\"\"` clears it. Stored verbatim." }
                },
                "required": ["epic"]
            }),
        },
        ToolDescriptor {
            name: "delete_epic",
            description: "Delete an epic's **registry entry only** (mirrors `tix epic delete --yes`). No issue is deleted or modified: the issues that referenced the epic keep their references, which then read as diagnosable missing references (C-005), and the child epics keep their recorded parent, which then reads as an unresolved parent. Returns the affected issues **and** the child epics — the CLI's impact report — so an agent can warn a user instead of orphaning work. The id is never reused. Requires `confirm: true`.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." },
                    "confirm": { "type": "boolean", "description": "Must be true. Acknowledges that only the registry entry is removed and neither an issue nor a child epic is deleted." }
                },
                "required": ["epic", "confirm"]
            }),
        },
        ToolDescriptor {
            name: "epic_hierarchy",
            description: "Navigate one epic's hierarchy (mirrors `tix epic navigate`). Resolved by its **stable id** (or a name). Returns the **raw values** in the service's order — the resolved `parent`, the direct `children`, the `ancestors` root-first, the `depth`, and the `unresolved` record when a recorded parent no longer exists — not a formatted summary, so a surface can compare by value (NFR-003). A member is an ordinary issue: direct issue navigation stays available (C-001). Read-only: no write, no History entry.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." }
                },
                "required": ["epic"]
            }),
        },
        ToolDescriptor {
            name: "epic_membership",
            description: "The derived, ordered membership of one epic (the `membership` operation of the epic service). Resolved by its **stable id** (or a name). Returns the raw `member_count` and the ordered member list — each member's issue number and its `#<number> <title>` display label — in the service's order (by issue number), never a formatted summary, so the value can be compared with the CLI's and the TUI's (NFR-003). Membership is derived from the issues' `epics` lists, never stored. Read-only: no write, no History entry.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." }
                },
                "required": ["epic"]
            }),
        },
        ToolDescriptor {
            name: "epic_health",
            description: "One epic's typed progress and health (the `rollup` operation of the epic service; the same value `tix epic show` renders). Resolved by its **stable id** (or a name). Returns the **raw** figures — `done`, `total`, `child_count`, the `weighted_progress` value, `blocked` with its member list, and `missing_references` — not a formatted summary, so parity is assertable by value (NFR-003). Every figure is computed once, in the service: `blocked` is consumed from the relationship service's derived read, never recomputed (C-008), and `missing_references` names every unresolvable reference of all five kinds exactly as lint reports it (C-005). Read-only: no write, no History entry.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." }
                },
                "required": ["epic"]
            }),
        },
        ToolDescriptor {
            name: "epic_associate_issues",
            description: "Reference one or more issues from an epic, each independently (mirrors `tix epic add` / `tix epic bulk-add`; the epic-side form of the bulk reference operation). The epic is resolved by its **stable id** once, up front, so an unknown epic refuses before any write. Each selector is processed independently through the store's service-backed reference write: one rejection is reported beside the rest rather than aborting them, and each accepted change appends exactly one History entry. A reference already present is an idempotent no-op that appends nothing.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name that resolves to the same epic." },
                    "issues": { "type": "string", "description": "Comma-separated issue selectors (a number, a filename, or a title fragment — as everywhere a selector is taken). One outcome is reported per selector." }
                },
                "required": ["epic", "issues"]
            }),
        },
        ToolDescriptor {
            name: "epic_dissociate_issues",
            description: "Remove an epic reference from one or more issues, each independently (mirrors `tix epic remove`). Drops only the reference — never an issue (C-006). The epic reference resolves to its **stable id** when it names a live epic and is otherwise used **verbatim**, which is the repair path for a stale stored id (C-005). Each selector is independent: a rejection is reported beside the rest rather than aborting them, and an absent reference is an idempotent no-op that appends nothing.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "epic": { "type": "string", "description": "Stable epic id, or a name. A reference that resolves to no live epic is used verbatim, so a stale stored id can still be removed." },
                    "issues": { "type": "string", "description": "Comma-separated issue selectors (a number, a filename, or a title fragment). One outcome is reported per selector." }
                },
                "required": ["epic", "issues"]
            }),
        },
        ToolDescriptor {
            name: "get_rollup",
            description: "An issue's parent rollup: its direct-children count, how many are in the board's completion column, and the typed progress value. The figures describe direct children only (`completion_basis`), and the counts are returned as numbers rather than only as rendered text, so a caller can compare them without parsing prose.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "issue": { "type": ["string", "integer"], "description": "Number, or a filename/title fragment." }
                },
                "required": ["issue"]
            }),
        },
        ToolDescriptor {
            name: "health_rollup",
            description: "A scope's derived health, with its contributors, its policy version and its diagnostics (`tix health rollup`). Rollups are policy-derived and read-only.",
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["scope", "id"],
                "properties": {
                    "scope": { "type": "string", "enum": ["issue", "project", "epic", "cycle", "milestone"], "description": "The scope kind." },
                    "id": { "type": "string", "description": "The scope's stable id (an issue number when `scope` is `issue`)." }
                }
            }),
        },
    ]
}