Skip to main content

heddle_cli_args/cli/cli_args/
commands_main.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Top-level CLI commands.
3
4use clap::{Args, Subcommand};
5
6#[cfg(feature = "client")]
7use super::AuthCommands;
8#[cfg(feature = "semantic")]
9use super::SemanticCommands;
10use super::{
11    AgentCommands, CompletionSubject, ContextCommands, DiscussCommands, HookCommands,
12    IntegrationCommands, OplogCommands, QueryArgs, RedactCommands, RemoteCommands, ReviewCommands,
13    ShellCommands, ThreadCommands, VisibilityCommands,
14    commands_args::{
15        AdoptArgs, CloneArgs, CollapseArgs, CommitArgs, DiffArgs, DoctorArgs, ExpandArgs, InitArgs,
16        LandArgs, LogArgs, PullArgs, PushArgs, ReadyArgs, ResolveArgs, RetroArgs, RevertArgs,
17        RunArgs, SnapshotArgs, SyncArgs, ThreadStartArgs, TimelineArgs, TryArgs, UndoArgs,
18        WatchArgs,
19    },
20};
21#[cfg(feature = "git-overlay")]
22use super::{ExportCommands, ImportCommands};
23
24#[derive(Clone, Debug, Args)]
25pub struct FsckArgs {
26    /// Full check (includes content verification).
27    #[arg(long)]
28    pub full: bool,
29
30    /// Run slower graph and signature integrity checks.
31    #[arg(long)]
32    pub thorough: bool,
33
34    /// Include Git projection, mapping, notes, and checkout checks.
35    #[arg(long)]
36    pub git: bool,
37
38    #[command(subcommand)]
39    pub command: Option<FsckCommands>,
40}
41
42#[derive(Clone, Debug, Subcommand)]
43pub enum FsckCommands {
44    /// Repair an integrity surface, then verify it.
45    Repair {
46        #[command(subcommand)]
47        target: FsckRepairCommands,
48    },
49}
50
51#[derive(Clone, Debug, Subcommand)]
52pub enum FsckRepairCommands {
53    /// Reconcile Git projection metadata or one projected ref.
54    Git(FsckRepairGitArgs),
55}
56
57#[derive(Clone, Debug, Args)]
58pub struct FsckRepairGitArgs {
59    /// Git ref to reconcile. Required for native repositories.
60    #[arg(long = "ref", value_name = "BRANCH")]
61    pub ref_name: Option<String>,
62
63    /// Assert the intended authority direction.
64    #[arg(long, value_parser = ["git", "heddle"])]
65    pub prefer: Option<String>,
66
67    /// Show the authority-valid repair without changing refs.
68    #[arg(long)]
69    pub preview: bool,
70}
71
72#[derive(Subcommand)]
73pub enum Commands {
74    /// Initialize Heddle in a directory or existing Git checkout.
75    Init(InitArgs),
76
77    /// Adopt Git history into Heddle-native source authority.
78    ///
79    /// Git Overlay is the normal existing-Git mode: Git keeps source objects,
80    /// refs, index, and worktree state while Heddle stores metadata in
81    /// `.heddle`. `adopt` imports history and moves source authority to Heddle.
82    Adopt(AdoptArgs),
83
84    /// Curated, progressive-disclosure help.
85    ///
86    /// `heddle help` prints the curated everyday verbs and points at
87    /// `heddle help advanced` for everything else. `heddle help
88    /// <topic>` prints the topic page (e.g. `daemon`, `signals`,
89    /// `bridge`). `heddle help <command path>` falls through to that
90    /// command's `--help` so the printer never duplicates clap's
91    /// per-verb derivation.
92    Help {
93        /// Topic name (`advanced`, `daemon`, `signals`, …) or command
94        /// path. When omitted, prints the curated default.
95        #[arg(value_name = "TOPIC_OR_COMMAND")]
96        topics: Vec<String>,
97    },
98
99    /// Show what needs attention and the next safe Heddle action.
100    #[command(after_help = "\
101Examples:
102  heddle status               # current thread, dirty paths, recommended next step
103  heddle status --short       # one-line summary for shell prompts
104  heddle status --watch       # live dashboard that refreshes in place
105")]
106    Status {
107        /// Short format.
108        #[arg(short, long)]
109        short: bool,
110
111        /// Continuously refresh status.
112        #[arg(long)]
113        watch: bool,
114
115        /// Internal helper for tests: stop after N watch updates.
116        #[arg(long, hide = true)]
117        watch_iterations: Option<usize>,
118
119        /// Internal helper for tests: polling interval in milliseconds.
120        #[arg(long, hide = true)]
121        watch_interval_ms: Option<u64>,
122    },
123
124    /// Stream live oplog activity.
125    ///
126    /// Tails the repository's append-only oplog file like `tail -f`,
127    /// emitting snapshots, merges, and thread events as they happen.
128    /// Exits on Ctrl-C.
129    Watch(WatchArgs),
130
131    /// Verify this workspace; exits nonzero until every check is clean.
132    #[command(after_help = "\
133Checks: Git mapping, worktree, remote, operation, clone verification, machine contract.
134
135Examples:
136  heddle verify                # strict verification gate and next recovery step
137  heddle verify --verbose      # full proof rows and machine-contract details
138  heddle verify --output json  # proof JSON when clean; error envelope when blocked
139")]
140    Verify,
141
142    /// Explain repository health, or run targeted doctor checks.
143    ///
144    /// `heddle doctor` (no subcommand) reports repository health and
145    /// the next recovery step. `heddle doctor docs` diff-checks markdown
146    /// documentation against
147    /// the actual CLI surface and exits non-zero on drift — wire it
148    /// into CI to stop docs from going stale.
149    Doctor(DoctorArgs),
150
151    /// Print the JSON Schema for a `--output json`-emitting verb.
152    ///
153    /// Contract-table introspection over CLI output shapes —
154    /// useful when wiring tools that consume `heddle <verb>
155    /// --output json` and want to validate or generate types. The schemas
156    /// live in `crates/cli-contract/src/cli/commands/schemas.rs`; the
157    /// command contract table registers available and documented
158    /// schema verbs for `heddle doctor schemas` drift detection
159    /// against `docs/json-schemas.md`.
160    ///
161    /// With no `<verb>`, prints the registered schema verbs. `<verb>`
162    /// is the joined subcommand path — e.g. `status`, `log`,
163    /// `fsck repair git`, `marker list`.
164    #[command(visible_alias = "schema")]
165    Schemas {
166        /// The verb whose schema to emit. Run `heddle schemas --help`
167        /// or look at `docs/json-schemas.md` for the registered list.
168        ///
169        /// `trailing_var_arg = true` lets the verb spec carry literal
170        /// `--flag` tokens (e.g. `heddle schemas log --reflog`,
171        /// `heddle schemas marker delete --prefix`) without clap
172        /// parsing them as options on `schemas` itself.
173        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
174        verb: Vec<String>,
175    },
176
177    /// Create or resume an isolated thread for focused work.
178    Start(ThreadStartArgs),
179
180    /// Run a command in a sandboxed ephemeral thread.
181    ///
182    /// Heddle creates a fresh thread with an isolated checkout, runs
183    /// `<cmd>` inside it, and then either captures the result on a
184    /// zero exit or drops the thread on a non-zero exit. The parent
185    /// thread's working tree is never touched — the ephemeral thread
186    /// is the sandbox. Implements item 3.1 from the heddle 6→8 plan.
187    ///
188    /// `try` is the **new-sandbox** sibling to `run`. Reach for `run`
189    /// when you already have a thread and just want to exec a command
190    /// inside its checkout (no thread creation, no capture, no
191    /// rollback).
192    Try(TryArgs),
193
194    /// Automation/workflow command: run a command inside an existing
195    /// thread's execution root.
196    ///
197    /// `run` is the **existing-thread** sibling to `try`. It looks up
198    /// the named (or current) thread, sets the child's cwd to that
199    /// thread's checkout, exports `HEDDLE_THREAD_*`, and runs `<cmd>`.
200    /// It does NOT create a thread, capture
201    /// state on success, or roll back on failure — those are `try`'s
202    /// job. Reach for `try` when you want the sandbox lifecycle; reach
203    /// for `run` when you already have a thread and just need to exec
204    /// inside it.
205    Run(RunArgs),
206
207    /// Automation/workflow command: refresh the current thread onto its target when safe.
208    Sync(SyncArgs),
209
210    /// Continue the active operation without remembering the specific subcommand.
211    Continue,
212
213    /// Abort the active operation without remembering the specific subcommand.
214    Abort,
215
216    /// Integrate a ready thread into its local target.
217    ///
218    /// `land` is the local integration verb: capture outstanding work if needed,
219    /// refresh against the target when safe, and land the thread. It fails
220    /// closed when conflicts or other blockers exist. Pair it with `ready`
221    /// when you want the verdict and next action before landing anything.
222    Land(LandArgs),
223
224    /// Prepare this thread for review or merge.
225    ///
226    /// `ready` captures outstanding work if needed, checks conflicts,
227    /// blockers, freshness, and semantic risk, then marks the thread
228    /// ready or blocked and prints the next action. It never lands,
229    /// checkpoints, or pushes; use it when you want Heddle's verdict
230    /// before integrating the work.
231    Ready(ReadyArgs),
232
233    /// Capture a recoverable Heddle step for undo, provenance, and review.
234    Capture(SnapshotArgs),
235
236    /// Commit the current captured state to the authoritative Git checkout.
237    Commit(CommitArgs),
238
239    /// Show state history.
240    ///
241    /// By default, when a thread name is given (e.g. `heddle log master`),
242    /// the walk is *first-parent only* — equivalent to `git log
243    /// --first-parent <branch>`. To see every ancestor reachable through
244    /// merge commits, pass `--graph` (which renders the full DAG) or
245    /// `--all` (which lists every state regardless of ancestry).
246    #[command(visible_alias = "history")]
247    Log(LogArgs),
248
249    /// Navigate, fork, reset, and recover agent tool-call timelines.
250    #[command(after_help = "\
251Examples:
252  heddle log --timeline
253  heddle timeline fork --tool-call call_123 --branch tlb-alt
254  heddle timeline reset --step tls-abc --materialize
255  heddle timeline recover
256")]
257    Timeline(TimelineArgs),
258
259    /// Show state details.
260    Show {
261        /// State by physical state ID, logical change ID, or unambiguous prefix.
262        /// Defaults to HEAD.
263        state: Option<String>,
264    },
265
266    /// Summarize a working session.
267    ///
268    /// Combines oplog, agent registry, marker, and context-annotation
269    /// reads into one structured payload — agent-readable retro of
270    /// captures, signals, and notable events since `--since`. Replaces
271    /// the reconstruct-from-`heddle log` boilerplate.
272    Retro(RetroArgs),
273
274    /// Show what changed in the worktree, a thread, or two states.
275    Diff(DiffArgs),
276
277    /// Open or resolve discussions anchored to symbols.
278    ///
279    /// Open a discussion against a symbol; append turns;
280    /// resolve by edit or dismiss. Anchors
281    /// travel across renames and cross-file moves on subsequent
282    /// state mutations.
283    #[command(after_help = "\
284Examples:
285  heddle discuss open src/auth.rs verify 'Should this reject expired tokens?'  # anchor a discussion
286  heddle discuss append <id> 'switched to argon2'          # add a turn
287  heddle discuss resolve <id> --mode by-edit --state HEAD
288")]
289    Discuss {
290        #[command(subcommand)]
291        command: DiscussCommands,
292    },
293
294    /// Structured query over the operation log. Filter by
295    /// actor, time window, signal kind, symbol, thread, verbs. Returns
296    /// structured results consumable by agents.
297    Query(QueryArgs),
298
299    /// Review a state — render the payload, sign, see signal health.
300    ///
301    /// `heddle review show` renders the review payload (summary,
302    /// agent narrative, in-budget signals, anchored discussions).
303    /// `heddle review sign` submits a `read` / `agent_preview` /
304    /// `agent_co_review` signature on the state. `heddle review
305    /// health` reports per-module signal fire rates over a rolling
306    /// window.
307    #[command(after_help = "\
308Examples:
309  heddle review show HEAD                                # render the review payload for HEAD
310  heddle review sign HEAD --kind read --public-key <hex> --signature <hex> --signed-at-unix <ts>
311  heddle review health --window 7                       # signal fire-rates over recent states
312")]
313    Review {
314        #[command(subcommand)]
315        command: ReviewCommands,
316    },
317
318    /// Redact a sensitive blob in a state so reads return a stub
319    /// instead of the content.
320    ///
321    /// `heddle redact apply` declares a redaction; the blob bytes stay
322    /// on disk and reads return the operator-supplied stub. `heddle
323    /// purge` afterward physically removes the bytes. Both are signed,
324    /// attributed, oplog-audited operations. See
325    /// `docs/PRINCIPLES.md` (the honesty principle) for context.
326    Redact {
327        #[command(subcommand)]
328        command: RedactCommands,
329    },
330
331    /// Declare and inspect a state's audience visibility tier.
332    ///
333    /// `heddle visibility set` binds a tier to a state; `promote` lifts it to
334    /// a less-restrictive tier via a superseding record; `show` reports the
335    /// effective tier; `list` enumerates non-public states. Capture binds the
336    /// inherited `[review.discussion] default_visibility` automatically
337    /// (Invariant A) — these verbs are the explicit operator overrides.
338    Visibility {
339        #[command(subcommand)]
340        command: VisibilityCommands,
341    },
342
343    /// Revert changes from a state.
344    Revert(RevertArgs),
345
346    /// Undo the last Heddle operation.
347    Undo(UndoArgs),
348
349    /// Collapse (squash) multiple states into one.
350    Collapse(CollapseArgs),
351
352    /// Expand a squashed land into the captures it collapsed.
353    Expand(ExpandArgs),
354
355    /// Manage threads.
356    Thread {
357        #[command(subcommand)]
358        command: ThreadCommands,
359    },
360
361    /// Shell integration helpers (auto-cd on thread start/switch/cd).
362    Shell {
363        #[command(subcommand)]
364        command: ShellCommands,
365    },
366
367    /// Internal shell-completion candidate helper.
368    #[command(name = "complete", alias = "__complete", hide = true)]
369    Complete {
370        /// Candidate set to print, one candidate per line.
371        #[arg(value_enum)]
372        subject: CompletionSubject,
373    },
374
375    /// Resolve merge conflicts.
376    Resolve(ResolveArgs),
377
378    /// Verify repository integrity or explicitly repair one surface.
379    Fsck(FsckArgs),
380
381    /// Inspect and repair the operation log.
382    ///
383    /// `heddle oplog recover` explicitly salvages a truncated or torn oplog,
384    /// reporting what was recovered — the operator-facing entrypoint over the
385    /// same recovery the everyday read path runs automatically.
386    Oplog {
387        #[command(subcommand)]
388        command: OplogCommands,
389    },
390
391    /// Import from another version control system.
392    #[cfg(feature = "git-overlay")]
393    Import {
394        #[command(subcommand)]
395        command: ImportCommands,
396    },
397
398    /// Export to another version control system.
399    #[cfg(feature = "git-overlay")]
400    Export {
401        #[command(subcommand)]
402        command: ExportCommands,
403    },
404
405    /// Push the source-authoritative history to a remote.
406    Push(PushArgs),
407
408    /// Pull source-authoritative history from a remote.
409    Pull(PullArgs),
410
411    /// Manage remote repositories.
412    Remote {
413        #[command(subcommand)]
414        command: RemoteCommands,
415    },
416
417    /// Authenticate with a Heddle server.
418    #[cfg(feature = "client")]
419    Auth {
420        #[command(subcommand)]
421        command: AuthCommands,
422    },
423
424    /// Report the acting identity (principal, token kind, scopes, operation
425    /// ceiling, TTL, signing status, server reachability).
426    #[cfg(feature = "client")]
427    #[command(after_help = "\
428Examples:
429  heddle whoami                       # human-readable identity summary
430  heddle whoami --output json         # machine-readable, stable output_kind shape
431  heddle whoami --server grpc.heddle.sh")]
432    Whoami {
433        /// Heddle server address (defaults to the configured server).
434        #[arg(long)]
435        server: Option<String>,
436    },
437
438    /// Manage code context annotations.
439    #[command(after_help = "\
440Examples:
441  heddle context set --path src/auth.rs --scope symbol:verify --kind invariant -m 'returns false on timing mismatch'
442  heddle context get --path src/auth.rs --scope symbol:verify
443  heddle context list --prefix src/auth          # everything attached under a path
444  heddle context check --path src/auth.rs        # surface annotations for editor tooling
445")]
446    Context {
447        #[command(subcommand)]
448        command: ContextCommands,
449    },
450
451    /// Manage ambient harness integrations.
452    Integration {
453        #[command(subcommand)]
454        command: IntegrationCommands,
455    },
456
457    /// Semantic analysis queries (call-graph hot-spots, churn,
458    /// signature-stability surfaces).
459    #[cfg(feature = "semantic")]
460    Semantic {
461        #[command(subcommand)]
462        command: SemanticCommands,
463    },
464
465    /// FUSE mount-daemon control plane — distinct from `agent`.
466    ///
467    /// `heddle daemon serve` runs a foreground mount daemon that
468    /// owns FUSE sessions for `--workspace virtualized --daemon`
469    /// threads. It is normally spawned on demand by the per-thread
470    /// CLI; running it interactively is for debugging.
471    /// `status` reports liveness/uptime/mount count without spawning;
472    /// `stop` asks a running daemon to drain mounts and exit.
473    Daemon {
474        #[command(subcommand)]
475        command: DaemonCommands,
476    },
477
478    /// Agent control surface — daemon lifecycle and reservation API.
479    ///
480    /// `heddle agent serve|status|stop` controls the local gRPC
481    /// daemon (Unix socket inside the repo). `heddle agent
482    /// reserve|capture|ready|release|list|heartbeat` is the stable
483    /// JSON contract orchestrators use to coordinate parallel
484    /// writers. Distinct from `heddle daemon` (FUSE mount control
485    /// plane) — different subsystem.
486    Agent {
487        #[command(subcommand)]
488        command: AgentCommands,
489    },
490
491    /// Inspect and refresh rebuildable performance sidecars.
492    Maintenance {
493        #[command(subcommand)]
494        command: MaintenanceCommands,
495    },
496
497    /// Clone from remote.
498    Clone(CloneArgs),
499
500    /// Manage repository hooks.
501    Hook {
502        #[command(subcommand)]
503        command: HookCommands,
504    },
505}
506
507/// Maintenance subcommands.
508#[derive(Clone, Debug, clap::Subcommand)]
509pub enum MaintenanceCommands {
510    /// Inspect repository performance sidecars and repo shape.
511    Inspect,
512
513    /// Refresh repository performance sidecars without changing repository meaning.
514    Refresh,
515
516    /// Garbage collect unreachable objects.
517    Gc {
518        /// Prune unreachable objects.
519        #[arg(long)]
520        prune: bool,
521
522        /// Aggressive garbage collection.
523        #[arg(long)]
524        aggressive: bool,
525
526        /// Show what would be removed without removing.
527        #[arg(long)]
528        dry_run: bool,
529    },
530}
531
532/// Daemon control plane subcommands. See `Commands::Daemon`.
533#[derive(Clone, Debug, clap::Subcommand)]
534pub enum DaemonCommands {
535    /// Run a foreground mount daemon for this repository.
536    ///
537    /// Normally spawned on demand by the per-thread CLI when
538    /// `--daemon` is passed. Running interactively is for
539    /// debugging the daemon protocol.
540    Serve,
541
542    /// Report daemon liveness, version, uptime, and active mount
543    /// count. No-op success when the daemon isn't running.
544    Status,
545
546    /// Ask the running daemon to drain its mounts and exit. Sweeps
547    /// any leftover registry entries with `fusermount -u` as a
548    /// safety net before returning.
549    Stop,
550}