Skip to main content

heddle_cli_args/cli/cli_args/
commands_thread.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Thread command definitions.
3
4use clap::{Args, Subcommand};
5
6use super::{
7    ThreadAbsorbArgs, ThreadApprovalsArgs, ThreadApproveArgs, ThreadCapturesArgs,
8    ThreadCheckMergeArgs, ThreadDropArgs, ThreadMoveArgs, ThreadNameArgs, ThreadPromoteArgs,
9    ThreadRenameArgs, ThreadResolveArgs, ThreadRevokeApprovalArgs, ThreadShowArgs,
10};
11
12#[derive(Subcommand, Clone)]
13pub enum ThreadCommands {
14    /// Create a thread ref at the current state.
15    #[command(after_help = "\
16Advanced split form:
17  heddle start <name> --path <dir> is the normal one-step isolated-checkout path.
18  heddle thread create <name> only creates the thread ref. Pair it later with
19  heddle thread promote <name> --path <dir> when you intentionally need to
20  create the ref now and materialize the checkout later.
21")]
22    Create {
23        /// Thread identifier.
24        name: String,
25        /// Mark the thread ephemeral. Auto-collapses after `--ttl` if not
26        /// promoted. The collapse is recorded as
27        /// `OpRecord::EphemeralThreadCollapse`; underlying states stay
28        /// addressable. (W1/A13.)
29        #[arg(long)]
30        ephemeral: bool,
31        /// TTL in seconds. Defaults to 24h when `--ephemeral` is set
32        /// without `--ttl`.
33        #[arg(long, requires = "ephemeral")]
34        ttl_secs: Option<u32>,
35    },
36
37    /// Print the name of the current thread (the thread the working
38    /// checkout is attached to). Read-only — no state change.
39    /// Useful in shell pipelines: `cd "$(heddle thread cd "$(heddle thread current)")"`.
40    Current,
41
42    /// Switch the current checkout to an existing thread ref.
43    Switch {
44        /// Thread identifier.
45        name: String,
46        /// Print only the target thread's checkout path on stdout and
47        /// exit. Used by the shell hook (`heddle shell init`) to auto-cd
48        /// into the new thread:
49        ///   dir=$(heddle thread switch X --print-cd-path) && cd "$dir"
50        /// Auto-capture still runs; rich output is suppressed.
51        #[arg(long, hide_short_help = true)]
52        print_cd_path: bool,
53        /// Discard uncommitted changes in the current checkout before switching.
54        #[arg(short, long)]
55        force: bool,
56    },
57
58    /// Print the on-disk path for a thread. Read-only — no state change,
59    /// no auto-capture. Pair with the shell hook (`heddle shell init`)
60    /// to land in the right directory:
61    ///   eval "$(heddle thread cd X)"
62    /// Or use the shell function directly: `heddle thread cd X` becomes
63    /// `cd <path>` when the hook is installed.
64    Cd {
65        /// Thread identifier.
66        name: String,
67    },
68
69    /// List threads.
70    List(ThreadListArgs),
71
72    /// Show one thread with actor and workflow context.
73    Show(ThreadShowArgs),
74
75    /// Show granular captures on a thread.
76    Captures(ThreadCapturesArgs),
77
78    /// Rename a thread ref.
79    Rename(ThreadRenameArgs),
80
81    /// Refresh a thread onto its target thread.
82    Refresh(ThreadNameArgs),
83
84    /// Move selected captured paths from one thread into another.
85    Move(ThreadMoveArgs),
86
87    /// Absorb a child thread into its parent or another thread.
88    Absorb(ThreadAbsorbArgs),
89
90    /// Guide a blocked or stale thread toward its next clean state.
91    Resolve(ThreadResolveArgs),
92
93    /// Materialize an existing thread ref at a chosen path.
94    #[command(after_help = "\
95Advanced split form:
96  heddle start <name> --path <dir> creates the thread ref and isolated checkout
97  in one step. `thread promote` is the second step after
98  `heddle thread create <name>` when you intentionally created the ref first
99  and want to materialize it later.
100")]
101    Promote(ThreadPromoteArgs),
102
103    /// Drop a thread and mark it abandoned.
104    #[command(visible_alias = "delete")]
105    Drop(ThreadDropArgs),
106
107    /// Record a merge approval for `<source> -> <target>`.
108    Approve(ThreadApproveArgs),
109
110    /// List approvals recorded for `<source> -> <target>`.
111    Approvals(ThreadApprovalsArgs),
112
113    /// Revoke a previously recorded approval by id.
114    RevokeApproval(ThreadRevokeApprovalArgs),
115
116    /// Check whether `<source> -> <target>` would merge under
117    /// the repo's branch-protection policies. Read-only.
118    CheckMerge(ThreadCheckMergeArgs),
119
120    /// Sweep merged, stale auto-created, or abandoned threads.
121    #[command(
122        long_about = "\
123Sweep threads that have outlived their usefulness. Cleanup removes recorded checkouts, marks matching thread records abandoned, and prunes live thread refs so everyday thread lists stay focused.
124
125Modes:
126  - --merged: clean up threads recorded as merged.
127  - --auto --older-than <duration>: clean up harness-created threads that have not been touched in the given duration.
128  - --abandoned: remove live refs and checkout residue left by abandoned threads; retain their records, states, and audit history.
129
130The three modes can be combined. Pair with --dry-run to preview the work without changing anything on disk.",
131        after_help = "\
132Examples:
133  heddle thread cleanup --merged --dry-run
134  heddle thread cleanup --merged
135  heddle thread cleanup --auto --older-than 7d --dry-run
136  heddle thread cleanup --abandoned --dry-run
137"
138    )]
139    Cleanup(ThreadCleanupArgs),
140
141    /// Manage named state markers under the thread namespace.
142    Marker {
143        #[command(subcommand)]
144        command: ThreadMarkerCommands,
145    },
146}
147
148#[derive(Subcommand, Clone)]
149pub enum ThreadMarkerCommands {
150    /// List markers, optionally filtered by name prefix.
151    ///
152    /// Pass `--filter <PREFIX>` to return only markers whose name
153    /// starts with the given prefix. The match is a literal
154    /// `starts_with` check, not a glob.
155    List {
156        /// Return only markers whose name starts with this prefix.
157        #[arg(long, value_name = "PREFIX")]
158        filter: Option<String>,
159    },
160
161    /// Create marker at current state.
162    Create {
163        /// Marker name.
164        name: String,
165    },
166
167    /// Delete marker(s).
168    ///
169    /// Pass an exact marker name, or `--prefix <PFX>` to delete every marker
170    /// whose name starts with the given prefix. Exactly one of `<NAME>` or
171    /// `--prefix` must be supplied.
172    Delete {
173        /// Marker name (exact match). Mutually exclusive with `--prefix`.
174        #[arg(required_unless_present = "prefix", conflicts_with = "prefix")]
175        name: Option<String>,
176
177        /// Delete every marker whose name starts with this prefix.
178        #[arg(long)]
179        prefix: Option<String>,
180    },
181
182    /// Show marker details.
183    Show {
184        /// Marker name.
185        name: String,
186    },
187}
188
189/// Arguments for `heddle thread list`.
190///
191/// The default view hides harness-auto-created and abandoned threads.
192/// Pass the corresponding include flags to surface them.
193#[derive(Args, Clone, Debug, Default)]
194pub struct ThreadListArgs {
195    /// Include threads created automatically by harness integrations
196    /// (e.g. Claude Code segment-rotation). Hidden by default to keep
197    /// the view focused on threads the user explicitly created.
198    #[arg(long)]
199    pub include_auto: bool,
200
201    /// Include threads whose lifecycle state is abandoned. Hidden by
202    /// default because they are no longer actionable.
203    #[arg(long)]
204    pub include_abandoned: bool,
205}
206
207/// Arguments for `heddle thread cleanup`.
208///
209/// At least one cleanup mode must be set; otherwise the command refuses
210/// with a clear message. `--older-than` is required when `--auto` is set.
211#[derive(Args, Clone, Debug)]
212pub struct ThreadCleanupArgs {
213    /// Clean up threads whose recorded state is `merged`.
214    #[arg(long)]
215    pub merged: bool,
216
217    /// Drop harness-auto-created threads (those tagged `auto: true`).
218    /// Combine with `--older-than` to gate the sweep on staleness.
219    #[arg(long)]
220    pub auto: bool,
221
222    /// Clean abandoned threads that still have a live ref or checkout
223    /// residue. States and audit history are retained.
224    #[arg(long)]
225    pub abandoned: bool,
226
227    /// Maximum age (since `updated_at`) for an auto-thread to be
228    /// considered live. Threads older than this are eligible for
229    /// sweep when `--auto` is set. Accepts a Go-style duration like
230    /// `7d`, `24h`, `30m`, `15s` (or a raw integer interpreted as
231    /// seconds).
232    #[arg(long, value_name = "DURATION")]
233    pub older_than: Option<String>,
234
235    /// Print what would be dropped without actually dropping it.
236    #[arg(long)]
237    pub dry_run: bool,
238}