Skip to main content

hivemind/cli/
commands.rs

1//! CLI command definitions.
2//!
3//! All features are accessible via CLI. The UI is a projection, not a controller.
4
5use super::output::OutputFormat;
6use clap::{Args, Parser, Subcommand};
7
8/// Hivemind CLI - Structured orchestration for agentic coding workflows.
9#[derive(Parser)]
10#[command(name = "hivemind")]
11#[command(
12    version,
13    about,
14    long_about = "CLI-first orchestration for agentic coding workflows.\n\nStart with: docs/overview/quickstart.md"
15)]
16#[command(propagate_version = true)]
17pub struct Cli {
18    /// Output format
19    #[arg(long, short = 'f', global = true, default_value = "table")]
20    pub format: OutputFormat,
21
22    /// Verbose output
23    #[arg(long, short = 'v', global = true)]
24    pub verbose: bool,
25
26    #[command(subcommand)]
27    pub command: Option<Commands>,
28}
29
30/// Top-level commands.
31#[derive(Subcommand)]
32pub enum Commands {
33    /// Show version information
34    Version,
35
36    /// Run the HTTP server (API + UI)
37    Serve(ServeArgs),
38
39    /// Project management commands
40    #[command(subcommand)]
41    Project(ProjectCommands),
42
43    /// Task management commands
44    #[command(subcommand)]
45    Task(TaskCommands),
46
47    /// Task graphs (planning): define tasks + dependencies as a DAG
48    #[command(subcommand)]
49    Graph(GraphCommands),
50
51    /// Task flows (execution): run a graph using a configured runtime adapter
52    #[command(subcommand)]
53    Flow(FlowCommands),
54
55    /// Event inspection commands
56    #[command(subcommand)]
57    Events(EventCommands),
58
59    /// Runtime adapter discovery and health checks
60    #[command(subcommand)]
61    Runtime(RuntimeCommands),
62
63    /// Verification commands
64    #[command(subcommand)]
65    Verify(VerifyCommands),
66
67    /// Merge commands
68    #[command(subcommand)]
69    Merge(MergeCommands),
70
71    /// Attempt inspection commands
72    #[command(subcommand)]
73    Attempt(AttemptCommands),
74
75    /// Checkpoint lifecycle commands
76    #[command(subcommand)]
77    Checkpoint(CheckpointCommands),
78
79    /// Inspect and manage git worktrees used for task execution
80    #[command(subcommand)]
81    Worktree(WorktreeCommands),
82}
83
84/// Checkpoint subcommands.
85#[derive(Subcommand)]
86pub enum CheckpointCommands {
87    /// Complete the currently active checkpoint for an attempt
88    Complete(CheckpointCompleteArgs),
89}
90
91/// Arguments for checkpoint completion.
92#[derive(Args)]
93pub struct CheckpointCompleteArgs {
94    /// Attempt ID (optional when `HIVEMIND_ATTEMPT_ID` is set)
95    #[arg(long)]
96    pub attempt_id: Option<String>,
97
98    /// Checkpoint ID
99    #[arg(long = "id")]
100    pub checkpoint_id: String,
101
102    /// Optional completion summary
103    #[arg(long)]
104    pub summary: Option<String>,
105}
106
107#[derive(Args)]
108pub struct ServeArgs {
109    #[arg(long, default_value = "127.0.0.1")]
110    pub host: String,
111
112    #[arg(long, default_value_t = 8787)]
113    pub port: u16,
114
115    #[arg(long, default_value_t = 200)]
116    pub events_limit: usize,
117}
118
119#[derive(Subcommand)]
120pub enum WorktreeCommands {
121    /// List worktree status for each task in a flow
122    List(WorktreeListArgs),
123    /// Inspect the worktree path and git metadata for a single task
124    Inspect(WorktreeInspectArgs),
125    /// Remove worktrees for a flow (best-effort)
126    Cleanup(WorktreeCleanupArgs),
127}
128
129#[derive(Args)]
130pub struct WorktreeListArgs {
131    /// Flow ID
132    pub flow_id: String,
133}
134
135#[derive(Args)]
136pub struct WorktreeInspectArgs {
137    /// Task ID
138    pub task_id: String,
139}
140
141#[derive(Args)]
142pub struct WorktreeCleanupArgs {
143    /// Flow ID
144    pub flow_id: String,
145}
146
147#[derive(Subcommand)]
148pub enum GraphCommands {
149    /// Create a new task graph from project tasks
150    Create(GraphCreateArgs),
151    /// Add a dependency edge to a draft graph (fails once graph is locked by a flow)
152    AddDependency(GraphAddDependencyArgs),
153    /// Add a verification check to a task in a draft graph
154    AddCheck(GraphAddCheckArgs),
155    /// Validate a graph for cycles and structural issues
156    Validate(GraphValidateArgs),
157    /// List graphs (optionally filtered by project)
158    List(GraphListArgs),
159}
160
161#[derive(Args)]
162pub struct GraphCreateArgs {
163    /// Project ID or name
164    pub project: String,
165    /// Human-friendly graph name
166    pub name: String,
167
168    /// Task IDs to include in the graph
169    #[arg(long, num_args = 1..)]
170    pub from_tasks: Vec<String>,
171}
172
173#[derive(Args)]
174pub struct GraphAddDependencyArgs {
175    /// Graph ID
176    pub graph_id: String,
177    /// Dependent task. Semantics: `from_task` depends on `to_task`.
178    pub from_task: String,
179    /// Dependency task. Semantics: `from_task` depends on `to_task`.
180    pub to_task: String,
181}
182
183#[derive(Args)]
184pub struct GraphAddCheckArgs {
185    /// Graph ID
186    pub graph_id: String,
187    /// Task ID within the graph
188    pub task_id: String,
189
190    /// Check name
191    #[arg(long)]
192    pub name: String,
193
194    /// Shell command to execute for the check
195    #[arg(long)]
196    pub command: String,
197
198    /// Whether this check is required for verification success
199    #[arg(long, default_value_t = true)]
200    pub required: bool,
201
202    /// Optional timeout in milliseconds for this check
203    #[arg(long)]
204    pub timeout_ms: Option<u64>,
205}
206
207#[derive(Args)]
208pub struct GraphValidateArgs {
209    /// Graph ID
210    pub graph_id: String,
211}
212
213#[derive(Args)]
214pub struct GraphListArgs {
215    /// Optional project ID or name to filter graphs
216    #[arg(long)]
217    pub project: Option<String>,
218}
219
220#[derive(Subcommand)]
221pub enum FlowCommands {
222    /// Create a new flow from a graph (locks the graph)
223    Create(FlowCreateArgs),
224    /// List flows (optionally filtered by project)
225    List(FlowListArgs),
226    /// Start a flow (transitions it into running state)
227    Start(FlowStartArgs),
228    /// Advance execution by one scheduling/execution step
229    Tick(FlowTickArgs),
230    /// Pause scheduling new tasks (running tasks may continue)
231    Pause(FlowPauseArgs),
232    /// Resume a paused flow
233    Resume(FlowResumeArgs),
234    /// Abort a flow
235    Abort(FlowAbortArgs),
236    /// Show flow state and per-task execution state
237    Status(FlowStatusArgs),
238}
239
240#[derive(Args)]
241pub struct FlowCreateArgs {
242    /// Graph ID
243    pub graph_id: String,
244    /// Optional flow name
245    #[arg(long)]
246    pub name: Option<String>,
247}
248
249#[derive(Args)]
250pub struct FlowListArgs {
251    /// Optional project ID or name to filter flows
252    #[arg(long)]
253    pub project: Option<String>,
254}
255
256#[derive(Args)]
257pub struct FlowStartArgs {
258    /// Flow ID
259    pub flow_id: String,
260}
261
262#[derive(Args)]
263pub struct FlowTickArgs {
264    /// Flow ID
265    pub flow_id: String,
266
267    /// Deprecated: interactive mode is no longer supported
268    #[arg(long)]
269    pub interactive: bool,
270
271    /// Override max tasks to schedule this tick (must be >= 1)
272    #[arg(long)]
273    pub max_parallel: Option<u16>,
274}
275
276#[derive(Args)]
277pub struct FlowPauseArgs {
278    /// Flow ID
279    pub flow_id: String,
280    /// If set, wait for running tasks to finish before returning
281    #[arg(long)]
282    pub wait: bool,
283}
284
285#[derive(Args)]
286pub struct FlowResumeArgs {
287    /// Flow ID
288    pub flow_id: String,
289}
290
291#[derive(Args)]
292pub struct FlowAbortArgs {
293    /// Flow ID
294    pub flow_id: String,
295    #[arg(long)]
296    pub force: bool,
297    #[arg(long)]
298    pub reason: Option<String>,
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
302pub enum TaskRetryMode {
303    Clean,
304    Continue,
305}
306
307#[derive(Args)]
308pub struct TaskRetryArgs {
309    pub task_id: String,
310    #[arg(long)]
311    pub reset_count: bool,
312    #[arg(long, value_enum, default_value_t = TaskRetryMode::Clean)]
313    pub mode: TaskRetryMode,
314}
315
316#[derive(Args)]
317pub struct TaskAbortArgs {
318    /// Task ID
319    pub task_id: String,
320    /// Optional abort reason
321    #[arg(long)]
322    pub reason: Option<String>,
323}
324
325#[derive(Args)]
326pub struct FlowStatusArgs {
327    /// Flow ID
328    pub flow_id: String,
329}
330
331/// Project subcommands.
332#[derive(Subcommand)]
333pub enum ProjectCommands {
334    /// Create a new project
335    Create(ProjectCreateArgs),
336
337    /// List all projects
338    List,
339
340    /// Show project details
341    Inspect(ProjectInspectArgs),
342
343    /// Update a project
344    Update(ProjectUpdateArgs),
345
346    /// Configure the runtime adapter used to execute `TaskFlows`
347    RuntimeSet(ProjectRuntimeSetArgs),
348
349    /// Attach a repository to a project
350    AttachRepo(AttachRepoArgs),
351
352    /// Detach a repository from a project
353    DetachRepo(DetachRepoArgs),
354}
355
356/// Arguments for project create.
357#[derive(Args)]
358pub struct ProjectCreateArgs {
359    /// Project name
360    pub name: String,
361
362    /// Project description
363    #[arg(long, short = 'd')]
364    pub description: Option<String>,
365}
366
367#[derive(Args)]
368pub struct ProjectRuntimeSetArgs {
369    /// Project ID or name
370    pub project: String,
371
372    /// Runtime adapter name (default: opencode)
373    #[arg(long, default_value = "opencode")]
374    pub adapter: String,
375
376    /// Path to the runtime binary (default: opencode)
377    #[arg(long, default_value = "opencode")]
378    pub binary_path: String,
379
380    /// Optional model identifier for the runtime (adapter-specific)
381    #[arg(long)]
382    pub model: Option<String>,
383
384    /// Extra args to pass to the runtime (repeatable)
385    #[arg(long = "arg", allow_hyphen_values = true)]
386    pub args: Vec<String>,
387
388    /// Extra environment variables for the runtime in KEY=VALUE form (repeatable).
389    /// Empty keys are rejected, empty values are allowed (KEY=), and duplicate keys use the last value.
390    #[arg(long = "env")]
391    pub env: Vec<String>,
392
393    /// Execution timeout in milliseconds
394    #[arg(long, default_value = "600000")]
395    pub timeout_ms: u64,
396
397    /// Max number of tasks that may execute concurrently per flow tick
398    #[arg(long, default_value_t = 1)]
399    pub max_parallel_tasks: u16,
400}
401
402/// Arguments for project inspect.
403#[derive(Args)]
404pub struct ProjectInspectArgs {
405    /// Project ID or name
406    pub project: String,
407}
408
409/// Arguments for project update.
410#[derive(Args)]
411pub struct ProjectUpdateArgs {
412    /// Project ID or name
413    pub project: String,
414
415    /// New name
416    #[arg(long)]
417    pub name: Option<String>,
418
419    /// New description
420    #[arg(long, short = 'd')]
421    pub description: Option<String>,
422}
423
424/// Arguments for attaching a repository.
425#[derive(Args)]
426pub struct AttachRepoArgs {
427    /// Project ID or name
428    pub project: String,
429
430    /// Path to the git repository
431    pub path: String,
432
433    /// Optional repository name override
434    #[arg(long)]
435    pub name: Option<String>,
436
437    /// Access mode (ro|rw)
438    #[arg(long, default_value = "rw")]
439    pub access: String,
440}
441
442/// Arguments for detaching a repository.
443#[derive(Args)]
444pub struct DetachRepoArgs {
445    /// Project ID or name
446    pub project: String,
447
448    /// Repository name
449    pub repo_name: String,
450}
451
452/// Task subcommands.
453#[derive(Subcommand)]
454pub enum TaskCommands {
455    /// Create a new task
456    Create(TaskCreateArgs),
457
458    /// List tasks in a project
459    List(TaskListArgs),
460
461    /// Show task details
462    Inspect(TaskInspectArgs),
463
464    /// Update a task
465    Update(TaskUpdateArgs),
466
467    /// Configure or clear a task-level runtime override
468    RuntimeSet(TaskRuntimeSetArgs),
469
470    /// Close a task
471    Close(TaskCloseArgs),
472
473    /// Start executing a ready task
474    Start(TaskStartArgs),
475    /// Mark a running task as complete
476    Complete(TaskCompleteArgs),
477
478    /// Request a retry for a failed task
479    Retry(TaskRetryArgs),
480    /// Abort a task within its flow
481    Abort(TaskAbortArgs),
482}
483
484#[derive(Args)]
485pub struct TaskStartArgs {
486    /// Task ID
487    pub task_id: String,
488}
489
490#[derive(Args)]
491pub struct TaskCompleteArgs {
492    /// Task ID
493    pub task_id: String,
494}
495
496/// Arguments for task create.
497#[derive(Args)]
498pub struct TaskCreateArgs {
499    /// Project ID or name
500    pub project: String,
501
502    /// Task title
503    pub title: String,
504
505    /// Task description
506    #[arg(long, short = 'd')]
507    pub description: Option<String>,
508
509    /// Scope contract as a JSON string.
510    /// Required shape: {"filesystem":{"rules":[...]},"repositories":[],"git":{"permissions":[]},"execution":{"allowed":[],"denied":[]}}
511    #[arg(long)]
512    pub scope: Option<String>,
513}
514
515/// Arguments for task list.
516#[derive(Args)]
517pub struct TaskListArgs {
518    /// Project ID or name
519    pub project: String,
520
521    /// Filter by state
522    #[arg(long)]
523    pub state: Option<String>,
524}
525
526/// Arguments for task inspect.
527#[derive(Args)]
528pub struct TaskInspectArgs {
529    /// Task ID
530    pub task_id: String,
531}
532
533/// Arguments for task update.
534#[derive(Args)]
535pub struct TaskUpdateArgs {
536    /// Task ID
537    pub task_id: String,
538
539    /// New title
540    #[arg(long)]
541    pub title: Option<String>,
542
543    /// New description
544    #[arg(long, short = 'd')]
545    pub description: Option<String>,
546}
547
548/// Arguments for task runtime override.
549#[derive(Args)]
550pub struct TaskRuntimeSetArgs {
551    /// Task ID
552    pub task_id: String,
553
554    /// Clear any existing task runtime override
555    #[arg(long)]
556    pub clear: bool,
557
558    /// Runtime adapter name
559    #[arg(long, default_value = "opencode")]
560    pub adapter: String,
561
562    /// Path to runtime binary
563    #[arg(long, default_value = "opencode")]
564    pub binary_path: String,
565
566    /// Optional model identifier
567    #[arg(long)]
568    pub model: Option<String>,
569
570    /// Extra args to pass to runtime
571    #[arg(long = "arg", allow_hyphen_values = true)]
572    pub args: Vec<String>,
573
574    /// Extra environment variables in KEY=VALUE form
575    #[arg(long = "env")]
576    pub env: Vec<String>,
577
578    /// Execution timeout in milliseconds
579    #[arg(long, default_value = "600000")]
580    pub timeout_ms: u64,
581}
582
583/// Runtime subcommands.
584#[derive(Subcommand)]
585pub enum RuntimeCommands {
586    /// List built-in runtime adapters and local availability
587    List,
588    /// Run runtime adapter health checks
589    Health(RuntimeHealthArgs),
590}
591
592/// Arguments for runtime health checks.
593#[derive(Args)]
594pub struct RuntimeHealthArgs {
595    /// Optional project ID or name to health-check configured runtime
596    #[arg(long)]
597    pub project: Option<String>,
598
599    /// Optional task ID to health-check effective runtime (task override or project default)
600    #[arg(long)]
601    pub task: Option<String>,
602}
603
604/// Arguments for task close.
605#[derive(Args)]
606pub struct TaskCloseArgs {
607    /// Task ID
608    pub task_id: String,
609
610    /// Optional reason
611    #[arg(long)]
612    pub reason: Option<String>,
613}
614
615/// Event subcommands.
616#[derive(Subcommand)]
617pub enum EventCommands {
618    /// List events
619    List(EventListArgs),
620
621    /// Show event details
622    Inspect(EventInspectArgs),
623
624    /// Stream events with filters
625    Stream(EventStreamArgs),
626
627    /// Replay events to reconstruct state
628    Replay(EventReplayArgs),
629}
630
631/// Arguments for event list.
632#[derive(Args)]
633pub struct EventListArgs {
634    /// Filter by project
635    #[arg(long)]
636    pub project: Option<String>,
637
638    /// Filter by graph ID
639    #[arg(long)]
640    pub graph: Option<String>,
641
642    /// Filter by flow ID
643    #[arg(long)]
644    pub flow: Option<String>,
645
646    /// Filter by task ID
647    #[arg(long)]
648    pub task: Option<String>,
649
650    /// Filter by attempt ID
651    #[arg(long)]
652    pub attempt: Option<String>,
653
654    /// Lower bound timestamp (RFC3339), inclusive
655    #[arg(long)]
656    pub since: Option<String>,
657
658    /// Upper bound timestamp (RFC3339), inclusive
659    #[arg(long)]
660    pub until: Option<String>,
661
662    /// Maximum number of events
663    #[arg(long, default_value = "50")]
664    pub limit: usize,
665}
666
667/// Arguments for event inspect.
668#[derive(Args)]
669pub struct EventInspectArgs {
670    /// Event ID
671    pub event_id: String,
672}
673
674/// Arguments for event stream.
675#[derive(Args)]
676pub struct EventStreamArgs {
677    /// Filter by flow ID
678    #[arg(long)]
679    pub flow: Option<String>,
680
681    /// Filter by task ID
682    #[arg(long)]
683    pub task: Option<String>,
684
685    /// Filter by project
686    #[arg(long)]
687    pub project: Option<String>,
688
689    /// Filter by graph ID
690    #[arg(long)]
691    pub graph: Option<String>,
692
693    /// Filter by attempt ID
694    #[arg(long)]
695    pub attempt: Option<String>,
696
697    /// Lower bound timestamp (RFC3339), inclusive
698    #[arg(long)]
699    pub since: Option<String>,
700
701    /// Upper bound timestamp (RFC3339), inclusive
702    #[arg(long)]
703    pub until: Option<String>,
704
705    /// Maximum number of events
706    #[arg(long, default_value = "100")]
707    pub limit: usize,
708}
709
710/// Arguments for event replay.
711#[derive(Args)]
712pub struct EventReplayArgs {
713    /// Flow ID to replay
714    pub flow_id: String,
715
716    /// Verify replayed state against current state
717    #[arg(long)]
718    pub verify: bool,
719}
720
721/// Verify subcommands.
722#[derive(Subcommand)]
723pub enum VerifyCommands {
724    /// Apply a human override to verification outcome for a task
725    Override(VerifyOverrideArgs),
726
727    /// Manually trigger verification for a task in an active flow
728    Run(VerifyRunArgs),
729
730    /// Show verification results for an attempt
731    Results(VerifyResultsArgs),
732}
733
734/// Arguments for verify override.
735#[derive(Args)]
736pub struct VerifyOverrideArgs {
737    /// Task ID
738    pub task_id: String,
739
740    /// Decision: pass or fail
741    pub decision: String,
742
743    /// Reason for override
744    #[arg(long)]
745    pub reason: String,
746}
747
748#[derive(Args)]
749pub struct VerifyRunArgs {
750    /// Task ID
751    pub task_id: String,
752}
753
754#[derive(Args)]
755pub struct VerifyResultsArgs {
756    /// Attempt ID
757    pub attempt_id: String,
758
759    /// Include full check output payloads
760    #[arg(long)]
761    pub output: bool,
762}
763
764/// Merge subcommands.
765#[derive(Subcommand)]
766pub enum MergeCommands {
767    #[command(
768        about = "Prepare a completed flow for integration (sandbox merge)",
769        long_about = "Prepare a completed flow for merge by building an integration sandbox, applying successful task branches, and running required integration checks before approval."
770    )]
771    Prepare(MergePrepareArgs),
772
773    #[command(
774        about = "Approve a prepared merge (explicit human gate)",
775        long_about = "Record the approving HUMAN user (HIVEMIND_USER or USER), ensure no unresolved conflicts/check failures remain, and emit MergeApproved so execute can proceed."
776    )]
777    Approve(MergeApproveArgs),
778
779    #[command(
780        about = "Execute an approved merge (fast-forward target)",
781        long_about = "Acquire the per-flow integration lock and fast-forward the chosen target branch from integration/<flow>/prepare, then clean up integration/<flow>/prepare, integration/<flow>/<task>, _integration_prepare, and exec branches. Emits MergeCompleted."
782    )]
783    Execute(MergeExecuteArgs),
784}
785
786/// Arguments for merge prepare.
787#[derive(Args)]
788pub struct MergePrepareArgs {
789    /// Flow ID
790    pub flow_id: String,
791
792    /// Target branch (set explicitly if your default branch is not 'main')
793    #[arg(long)]
794    pub target: Option<String>,
795}
796
797/// Arguments for merge approve.
798#[derive(Args)]
799pub struct MergeApproveArgs {
800    /// Flow ID
801    pub flow_id: String,
802}
803
804/// Arguments for merge execute.
805#[derive(Args)]
806pub struct MergeExecuteArgs {
807    /// Flow ID
808    pub flow_id: String,
809}
810
811/// Attempt subcommands.
812#[derive(Subcommand)]
813pub enum AttemptCommands {
814    /// Inspect an attempt
815    Inspect(AttemptInspectArgs),
816}
817
818/// Arguments for attempt inspect.
819#[derive(Args)]
820pub struct AttemptInspectArgs {
821    /// Attempt ID
822    pub attempt_id: String,
823
824    /// Show retry context
825    #[arg(long)]
826    pub context: bool,
827
828    /// Show changes diff
829    #[arg(long)]
830    pub diff: bool,
831
832    /// Show runtime output
833    #[arg(long)]
834    pub output: bool,
835}