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 = "git-overlay")]
7use super::BridgeCommands;
8#[cfg(feature = "semantic")]
9use super::SemanticCommands;
10use super::{
11 AgentCommands, CompletionSubject, ContextCommands, DiscussCommands, EnvCommands, HookCommands,
12 IntegrationCommands, OplogCommands, QueryArgs, RedactCommands, RemoteCommands, ReviewCommands,
13 ShellCommands, ThreadCommands, VisibilityCommands,
14 commands_args::{
15 AdoptArgs, CloneArgs, DiffArgs, DoctorArgs, INIT_VERB, InitArgs, LandArgs, LogArgs,
16 PullArgs, PushArgs, ReadyArgs, ResolveArgs, RevertArgs, SnapshotArgs, SyncArgs,
17 ThreadStartArgs, UndoArgs, WatchArgs,
18 },
19};
20#[cfg(feature = "client")]
21use super::{AuthCommands, ClaimArgs, PromoteArgs};
22
23#[derive(Clone, Debug, Args)]
24pub struct FsckArgs {
25 /// Full check (includes content verification).
26 #[arg(long)]
27 pub full: bool,
28
29 /// Run slower graph and signature integrity checks.
30 #[arg(long)]
31 pub thorough: bool,
32
33 /// Verify offline authorship identity and review-signature chains.
34 #[arg(long, requires = "thorough")]
35 pub provenance: bool,
36
37 /// Include Git projection, mapping, notes, and checkout checks.
38 #[arg(long)]
39 pub git: bool,
40
41 #[command(subcommand)]
42 pub command: Option<FsckCommands>,
43}
44
45#[derive(Clone, Debug, Subcommand)]
46pub enum FsckCommands {
47 /// Repair an integrity surface, then verify it.
48 Repair {
49 #[command(subcommand)]
50 target: FsckRepairCommands,
51 },
52}
53
54#[derive(Clone, Debug, Subcommand)]
55pub enum FsckRepairCommands {
56 /// Reconcile Git projection metadata or one projected ref.
57 Git(FsckRepairGitArgs),
58}
59
60#[derive(Clone, Debug, Args)]
61pub struct FsckRepairGitArgs {
62 /// Git ref to reconcile. Required for native repositories.
63 #[arg(long = "ref", value_name = "BRANCH")]
64 pub ref_name: Option<String>,
65
66 /// Assert the intended authority direction.
67 #[arg(long, value_parser = ["git", "heddle"])]
68 pub prefer: Option<String>,
69
70 /// Show the authority-valid repair without changing refs.
71 #[arg(long)]
72 pub preview: bool,
73}
74
75#[derive(Subcommand)]
76pub enum Commands {
77 /// Initialize Heddle in a directory or existing Git checkout.
78 #[command(name = INIT_VERB)]
79 Init(InitArgs),
80
81 /// Adopt Git history into Heddle-native source authority.
82 ///
83 /// Git Overlay is the normal existing-Git mode: Git keeps source objects,
84 /// refs, index, and worktree state while Heddle stores metadata in
85 /// `.heddle`. `adopt` imports history and moves source authority to Heddle.
86 Adopt(AdoptArgs),
87
88 /// Curated, progressive-disclosure help.
89 ///
90 /// `heddle help` prints the locked everyday verbs. `heddle help
91 /// <topic>` prints the topic page (e.g. `model`, `daemon`,
92 /// `signals`, `git-concepts`). `heddle help <command path>` falls
93 /// through to that command's `--help` so the printer never
94 /// duplicates clap's per-verb derivation.
95 Help {
96 /// Topic name (`model`, `daemon`, `signals`, …) or command
97 /// path. When omitted, prints the curated default.
98 #[arg(value_name = "TOPIC_OR_COMMAND")]
99 topics: Vec<String>,
100 },
101
102 /// Show what needs attention and the next safe Heddle action.
103 #[command(after_help = "\
104Examples:
105 heddle status # current thread, dirty paths, recommended next step
106 heddle status --short # one-line summary for shell prompts
107 heddle status --watch # live dashboard that refreshes in place
108")]
109 Status {
110 /// Short format.
111 #[arg(short, long)]
112 short: bool,
113
114 /// Continuously refresh status.
115 #[arg(long)]
116 watch: bool,
117
118 /// Internal helper for tests: stop after N watch updates.
119 #[arg(long, hide = true)]
120 watch_iterations: Option<usize>,
121
122 /// Internal helper for tests: polling interval in milliseconds.
123 #[arg(long, hide = true)]
124 watch_interval_ms: Option<u64>,
125 },
126
127 /// Stream live oplog activity.
128 ///
129 /// Tails the repository's append-only oplog file like `tail -f`,
130 /// emitting snapshots, merges, and thread events as they happen.
131 /// Exits on Ctrl-C.
132 Watch(WatchArgs),
133
134 /// Verify this workspace; exits nonzero until every check is clean.
135 #[command(after_help = "\
136Checks: Git mapping, worktree, remote, operation, clone verification, machine contract.
137
138Examples:
139 heddle verify # strict verification gate and next recovery step
140 heddle verify --verbose # full proof rows and machine-contract details
141 heddle verify --output json # proof JSON when clean; error envelope when blocked
142")]
143 Verify {
144 /// Verify each state's offline authorship and review-signature chain.
145 #[arg(long)]
146 provenance: bool,
147 },
148
149 /// Explain repository health, or run targeted doctor checks.
150 ///
151 /// `heddle doctor` (no subcommand) reports repository health and
152 /// the next recovery step. `heddle doctor docs` diff-checks markdown
153 /// documentation against
154 /// the actual CLI surface and exits non-zero on drift — wire it
155 /// into CI to stop docs from going stale.
156 Doctor(DoctorArgs),
157
158 /// Create or resume an isolated thread for focused work.
159 Start(ThreadStartArgs),
160
161 /// Run Heddle CI checks. Finds `ci.ts` / `ci.rs` / `ci.go`, compiles if needed, then runs.
162 #[cfg(feature = "ci")]
163 Ci {
164 #[command(subcommand)]
165 command: super::CiCommands,
166 },
167
168 /// Automation/workflow command: refresh the current thread onto its target when safe.
169 Sync(SyncArgs),
170
171 /// Continue the active operation without remembering the specific subcommand.
172 Continue,
173
174 /// Abort the active operation without remembering the specific subcommand.
175 Abort,
176
177 /// Integrate a ready thread into its local target.
178 ///
179 /// `land` is the local integration verb: capture outstanding work if needed,
180 /// refresh against the target when safe, and land the thread. It fails
181 /// closed when conflicts or other blockers exist. Pair it with `ready`
182 /// when you want the verdict and next action before landing anything.
183 Land(LandArgs),
184
185 /// Prepare this thread for review or merge.
186 ///
187 /// `ready` captures outstanding work if needed, checks conflicts,
188 /// blockers, freshness, and semantic risk, then marks the thread
189 /// ready or blocked and prints the next action. It never lands,
190 /// checkpoints, or pushes; use it when you want Heddle's verdict
191 /// before integrating the work.
192 Ready(ReadyArgs),
193
194 /// Capture a recoverable Heddle step for undo, provenance, and review.
195 Capture(SnapshotArgs),
196
197 /// Show state history.
198 ///
199 /// By default, when a thread name is given (e.g. `heddle log master`),
200 /// the walk is *first-parent only* — equivalent to `git log
201 /// --first-parent <branch>`. To see every ancestor reachable through
202 /// merge commits, pass `--graph` (which renders the full DAG) or
203 /// `--all` (which lists every state regardless of ancestry).
204 #[command(visible_alias = "history")]
205 Log(LogArgs),
206
207 /// Show state details.
208 Show {
209 /// State by physical state ID, logical change ID, or unambiguous prefix.
210 /// Defaults to HEAD.
211 state: Option<String>,
212 },
213
214 /// Show what changed in the worktree, a thread, or two states.
215 Diff(DiffArgs),
216
217 /// Open or resolve discussions anchored to symbols.
218 ///
219 /// Open a discussion against a symbol; append turns;
220 /// resolve by edit or dismiss. Anchors
221 /// travel across renames and cross-file moves on subsequent
222 /// state mutations.
223 ///
224 /// Native Heddle only. Discussions live in `.heddle` and travel
225 /// over `heddle push` / `heddle pull` to a Heddle remote. They are
226 /// not projected into Git, so `git clone` does not carry them; in
227 /// Git Overlay mode they are local to that working copy.
228 #[command(after_help = "\
229Scope:
230 Native Heddle only. Discussions are stored in `.heddle` and move over
231 `heddle push` / `heddle pull`. Git does not carry them: a `git clone` of a
232 Git Overlay repository arrives with no discussions and no Heddle store.
233
234Examples:
235 heddle discuss open src/auth.rs verify 'Should this reject expired tokens?' # anchor a discussion
236 heddle discuss append <id> 'switched to argon2' # add a turn
237 heddle discuss resolve <id> --mode by-edit --state HEAD
238")]
239 Discuss {
240 #[command(subcommand)]
241 command: DiscussCommands,
242 },
243
244 /// Structured query over the operation log. Filter by
245 /// actor, time window, signal kind, symbol, thread, verbs. Returns
246 /// structured results consumable by agents.
247 Query(QueryArgs),
248
249 /// Review a state — render the payload, sign, see signal health.
250 ///
251 /// `heddle review show` renders the review payload (summary,
252 /// agent narrative, in-budget signals, anchored discussions).
253 /// `heddle review sign` submits a `read` / `agent_preview` /
254 /// `agent_co_review` signature on the state. `heddle review
255 /// health` reports per-module signal fire rates over a rolling
256 /// window.
257 #[command(after_help = "\
258Examples:
259 heddle review show HEAD # render the review payload for HEAD
260 heddle review show HEAD --base last-turn # review this agent peer's turn
261 heddle review sign HEAD --kind read --public-key <hex> --signature <hex> --signed-at-unix <ts>
262 heddle review health --window 7 # signal fire-rates over recent states
263")]
264 Review {
265 #[command(subcommand)]
266 command: ReviewCommands,
267 },
268
269 /// Redact a sensitive blob in a state so reads return a stub
270 /// instead of the content.
271 ///
272 /// `heddle redact apply` declares a redaction; the blob bytes stay
273 /// on disk and reads return the operator-supplied stub. `heddle
274 /// redact purge` afterward physically removes the bytes. Both are signed,
275 /// attributed, oplog-audited operations. See
276 /// `docs/PRINCIPLES.md` (the honesty principle) for context.
277 Redact {
278 #[command(subcommand)]
279 command: RedactCommands,
280 },
281
282 /// Declare and inspect a state's audience visibility tier.
283 ///
284 /// `heddle visibility set` binds a tier to a state; `promote` lifts it to
285 /// a less-restrictive tier via a superseding record; `show` reports the
286 /// effective tier; `list` enumerates non-public states. Capture binds the
287 /// inherited `[review.discussion] default_visibility` automatically
288 /// (Invariant A) — these verbs are the explicit operator overrides.
289 Visibility {
290 #[command(subcommand)]
291 command: VisibilityCommands,
292 },
293
294 /// Run a child with a confidential runtime profile.
295 ///
296 /// `heddle env run --profile <name> -- <cmd>` asks the local policy
297 /// broker to unwrap named slots and injects them into the child
298 /// environment only. Values never land in the worktree, the store, or
299 /// command JSON. Same-UID callers are cooperative; OS isolation is later.
300 #[command(after_help = "\
301Examples:
302 heddle env list
303 heddle env create --name local --from-env DATABASE_URL
304 heddle env run --profile local -- printenv DATABASE_URL
305")]
306 Env {
307 #[command(subcommand)]
308 command: EnvCommands,
309 },
310
311 /// Revert changes from a state.
312 Revert(RevertArgs),
313
314 /// Undo the last Heddle operation.
315 Undo(UndoArgs),
316
317 /// Manage threads.
318 Thread {
319 #[command(subcommand)]
320 command: ThreadCommands,
321 },
322
323 /// Shell integration helpers (auto-cd on thread start/switch/cd).
324 Shell {
325 #[command(subcommand)]
326 command: ShellCommands,
327 },
328
329 /// Print a tab-completion script for bash, zsh, or fish.
330 ///
331 /// With no shell, prints install lines. With `bash`, `zsh`, or `fish`,
332 /// emits the same script as `heddle shell completion`.
333 Completions {
334 /// Shell to generate completion for: bash, zsh, or fish.
335 #[arg(value_name = "SHELL")]
336 shell: Option<String>,
337 },
338
339 /// Internal shell-completion candidate helper.
340 #[command(name = "complete", alias = "__complete", hide = true)]
341 Complete {
342 /// Candidate set to print, one candidate per line.
343 #[arg(value_enum)]
344 subject: CompletionSubject,
345 },
346
347 /// Resolve merge conflicts.
348 Resolve(ResolveArgs),
349
350 /// Explicit interoperability with other version-control formats.
351 #[cfg(feature = "git-overlay")]
352 Bridge {
353 #[command(subcommand)]
354 command: BridgeCommands,
355 },
356
357 /// Push the source-authoritative history to a remote.
358 Push(PushArgs),
359
360 /// Pull source-authoritative history from a remote.
361 Pull(PullArgs),
362
363 /// Manage remote repositories.
364 Remote {
365 #[command(subcommand)]
366 command: RemoteCommands,
367 },
368
369 /// Authenticate with a Heddle server.
370 #[cfg(feature = "client")]
371 Auth {
372 #[command(subcommand)]
373 command: AuthCommands,
374 },
375
376 /// Promote a personal hosted spool to a root-level spool.
377 ///
378 /// Moves `spool/<your-handle>/<name>` to `spool/<name>` after the server
379 /// confirms the root slug is free, the account is claimed/verified, and
380 /// you hold an owner grant. Clone a bare name still prefers your personal
381 /// copy first.
382 #[cfg(feature = "client")]
383 Promote(PromoteArgs),
384
385 /// Offer this agent account for a human to claim.
386 ///
387 /// Prints a short-lived bearer link, then keeps the agent's Iroh endpoint
388 /// online until the human finishes, the offer expires, or Ctrl-C stops it.
389 #[cfg(feature = "client")]
390 #[command(after_help = "\
391Examples:
392 heddle claim
393 heddle claim --timeout 30m
394 heddle claim --server weft.example --web-origin https://heddle.example
395")]
396 Claim(ClaimArgs),
397
398 /// Report the capture actor, then hosted auth.
399 ///
400 /// The capture actor is who the next capture is attributed to
401 /// (`user_config`, `init --principal-*`, or `HEDDLE_PRINCIPAL_*`).
402 /// Hosted auth is whether this machine has a server credential.
403 /// These are different objects. `heddle auth login` does not set the
404 /// local actor. `whoami` only reads; it never attaches a credential.
405 #[cfg(feature = "client")]
406 #[command(after_help = "\
407The capture actor and hosted auth are different objects:
408 capture actor who the next capture is attributed to
409 (user_config, init --principal-*, or HEDDLE_PRINCIPAL_*)
410 hosted auth whether this machine has a credential for the server
411 (heddle auth login). whoami never attaches a credential.
412
413Examples:
414 heddle whoami # capture actor first, then hosted auth
415 heddle whoami --output json # machine-readable, stable output_kind shape
416 heddle whoami --server api.heddle.sh")]
417 Whoami {
418 /// Heddle server address (defaults to the configured server).
419 #[arg(long)]
420 server: Option<String>,
421 },
422
423 /// Manage code context annotations.
424 ///
425 /// Native Heddle only. Annotations live in `.heddle`, and travel
426 /// over `heddle push` / `heddle pull` to a Heddle remote. They are
427 /// deliberately not projected into Git — not into `refs/notes/*`,
428 /// not into a tracked file — so `git push` and `git clone` do not
429 /// carry them. In Git Overlay mode annotations still work and are
430 /// still useful; they are simply local to that working copy.
431 #[command(after_help = "\
432Scope:
433 Native Heddle only. Annotations are stored in `.heddle` and move over
434 `heddle push` / `heddle pull`. Git does not carry them: a `git clone` of a
435 Git Overlay repository arrives with no annotations and no Heddle store.
436
437Examples:
438 heddle context set --path src/auth.rs --scope symbol:verify --kind invariant -m 'returns false on timing mismatch'
439 heddle context get --path src/auth.rs --scope symbol:verify
440 heddle context list --prefix src/auth # everything attached under a path
441 heddle context check --path src/auth.rs # surface annotations for editor tooling
442")]
443 Context {
444 #[command(subcommand)]
445 command: ContextCommands,
446 },
447
448 /// Manage ambient harness integrations.
449 Integration {
450 #[command(subcommand)]
451 command: IntegrationCommands,
452 },
453
454 /// Semantic analysis queries (call-graph hot-spots, churn,
455 /// signature-stability surfaces).
456 #[cfg(feature = "semantic")]
457 Semantic {
458 #[command(subcommand)]
459 command: SemanticCommands,
460 },
461
462 /// FUSE mount-daemon control plane — distinct from `agent`.
463 ///
464 /// `heddle daemon serve` runs a foreground mount daemon that
465 /// owns FUSE sessions for `--workspace virtualized --daemon`
466 /// threads. It is normally spawned on demand by the per-thread
467 /// CLI; running it interactively is for debugging.
468 /// `status` reports liveness/uptime/mount count without spawning;
469 /// `stop` asks a running daemon to drain mounts and exit.
470 Daemon {
471 #[command(subcommand)]
472 command: DaemonCommands,
473 },
474
475 /// Box-scoped network daemon control plane — distinct from
476 /// `daemon` (FUSE mounts).
477 ///
478 /// `heddle netd serve` runs a long-lived async daemon that binds
479 /// the machine's single persistent Iroh endpoint on the persisted
480 /// device node id and keeps it relay-reachable, so outstanding
481 /// claim links keep resolving across restarts. Unlike `daemon`, it
482 /// is not gated on Linux/FUSE and never idle-exits. `status`
483 /// reports liveness and the advertised node id; `stop` asks a
484 /// running daemon to close its endpoint and exit.
485 Netd {
486 #[command(subcommand)]
487 command: NetdCommands,
488 },
489
490 /// Agent reservation and one-shot orchestration API.
491 ///
492 /// `heddle agent reserve|capture|ready|release|list|heartbeat` is the stable
493 /// JSON contract orchestrators use to coordinate parallel
494 /// writers. `heddle daemon` remains the distinct FUSE mount control plane.
495 Agent {
496 #[command(subcommand)]
497 command: AgentCommands,
498 },
499
500 /// Inspect and refresh rebuildable performance sidecars.
501 Maintenance {
502 #[command(subcommand)]
503 command: MaintenanceCommands,
504 },
505
506 /// Clone from remote.
507 Clone(CloneArgs),
508
509 /// Manage repository hooks.
510 Hook {
511 #[command(subcommand)]
512 command: HookCommands,
513 },
514}
515
516/// Maintenance subcommands.
517#[derive(Clone, Debug, clap::Subcommand)]
518pub enum MaintenanceCommands {
519 /// Verify repository integrity or explicitly repair one surface.
520 Fsck(FsckArgs),
521
522 /// Inspect repository performance sidecars and repo shape.
523 Inspect,
524
525 /// Refresh repository performance sidecars without changing repository meaning.
526 Refresh,
527
528 /// Repack native objects now through the resource-controlled scheduler.
529 Repack,
530
531 /// Garbage collect unreachable objects.
532 Gc {
533 /// Prune unreachable objects.
534 #[arg(long)]
535 prune: bool,
536
537 /// Aggressive garbage collection.
538 #[arg(long)]
539 aggressive: bool,
540
541 /// Show what would be removed without removing.
542 #[arg(long)]
543 dry_run: bool,
544 },
545
546 /// Inspect and repair the operation log.
547 ///
548 /// `heddle maintenance oplog recover` explicitly salvages a truncated or
549 /// torn oplog, reporting what was recovered — the operator-facing
550 /// entrypoint over the same recovery the everyday read path runs
551 /// automatically.
552 Oplog {
553 #[command(subcommand)]
554 command: OplogCommands,
555 },
556}
557
558/// Daemon control plane subcommands. See `Commands::Daemon`.
559#[derive(Clone, Debug, clap::Subcommand)]
560pub enum DaemonCommands {
561 /// Run a foreground mount daemon for this repository.
562 ///
563 /// Normally spawned on demand by the per-thread CLI when
564 /// `--daemon` is passed. Running interactively is for
565 /// debugging the daemon protocol.
566 Serve,
567
568 /// Report daemon liveness, version, uptime, and active mount
569 /// count. No-op success when the daemon isn't running.
570 Status,
571
572 /// Ask the running daemon to drain its mounts and exit. Sweeps
573 /// any leftover registry entries with `fusermount -u` as a
574 /// safety net before returning.
575 Stop,
576}
577
578/// Box-scoped network daemon subcommands. See `Commands::Netd`.
579#[derive(Clone, Debug, clap::Subcommand)]
580pub enum NetdCommands {
581 /// Run the foreground network daemon: bind the persistent device
582 /// endpoint, keep relays online, and serve same-uid control RPCs.
583 /// Never idle-exits.
584 Serve,
585
586 /// Report network-daemon liveness and the advertised device node
587 /// id. No-op success when the daemon isn't running.
588 Status,
589
590 /// Ask the running network daemon to close its endpoint and exit.
591 Stop,
592}