use clap::{Parser, Subcommand, ValueEnum};
#[derive(Parser)]
#[command(name = "jjj")]
#[command(author, version)]
#[command(about = "Distributed project management for Jujutsu repositories")]
#[command(long_about = "
jjj organises work as Problems → Solutions → Critiques, stored in an orphaned
'jjj' bookmark alongside your code. No central server, no database — everything
syncs with your normal 'jj git push'.
Problems What needs solving (the question)
Solutions Conjectures attached to jj change IDs (the approach)
Critiques Error-elimination feedback that blocks or endorses a solution
Milestones Time-based goals grouping related problems")]
#[command(after_long_help = "TYPICAL WORKFLOW:
jjj init Set up a new repository
jjj problem new 'Bug: ...' Define what needs solving
jjj solution new 'Fix: ...' Propose your approach (creates a solution record)
jjj solution attach <id> Link your current jj change to the solution
jjj solution submit <id> Submit the solution for review
jjj critique new <id> '...' Raise a critique against a solution
jjj solution approve <id> Approve the solution once critiques are resolved
Run 'jjj <command> --help' for detailed options.")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
#[command(display_order = 0)]
Ui,
#[command(display_order = 1)]
Status {
#[arg(long)]
all: bool,
#[arg(long)]
mine: bool,
#[arg(long)]
limit: Option<usize>,
#[arg(long)]
json: bool,
},
#[command(display_order = 2)]
Next {
#[arg(long)]
top: Option<usize>,
#[arg(long)]
mine: bool,
#[arg(long)]
json: bool,
#[arg(long)]
claim: bool,
},
#[command(display_order = 10)]
Problem {
#[command(subcommand)]
action: ProblemAction,
},
#[command(display_order = 11)]
Solution {
#[command(subcommand)]
action: SolutionAction,
},
#[command(display_order = 12)]
Critique {
#[command(subcommand)]
action: CritiqueAction,
},
#[command(display_order = 13)]
Milestone {
#[command(subcommand)]
action: MilestoneAction,
},
#[command(display_order = 30)]
Search {
query: String,
#[arg(long, short = 't')]
r#type: Option<String>,
#[arg(long)]
text_only: bool,
#[arg(long)]
json: bool,
},
#[command(display_order = 31)]
Timeline {
problem_id: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 3)]
Overlaps {
#[arg(long)]
json: bool,
},
#[command(display_order = 4)]
Insights {
#[arg(long)]
json: bool,
},
#[command(display_order = 32)]
Events {
#[command(subcommand)]
action: Option<EventsAction>,
#[arg(long)]
from: Option<String>,
#[arg(long)]
to: Option<String>,
#[arg(long)]
problem: Option<String>,
#[arg(long)]
solution: Option<String>,
#[arg(long, name = "type")]
event_type: Option<String>,
#[arg(long)]
search: Option<String>,
#[arg(long)]
since: Option<String>,
#[arg(long)]
json: bool,
#[arg(long, default_value = "20")]
limit: usize,
},
#[command(display_order = 40)]
Fetch {
#[arg(long, default_value = "origin")]
remote: String,
},
#[command(display_order = 41)]
Push {
bookmarks: Vec<String>,
#[arg(long, default_value = "origin")]
remote: String,
#[arg(long)]
no_prompt: bool,
#[arg(long)]
dry_run: bool,
},
#[command(display_order = 42)]
Sync {
#[arg(long, default_value = "origin")]
remote: String,
#[arg(long)]
no_prompt: bool,
#[arg(long)]
dry_run: bool,
},
#[command(display_order = 43)]
Github {
#[command(subcommand)]
action: Option<GitHubSyncAction>,
#[arg(long)]
dry_run: bool,
},
#[command(display_order = 33)]
Tags {
#[arg(long)]
json: bool,
},
#[command(display_order = 34)]
Rank {
#[command(subcommand)]
action: RankAction,
},
#[command(display_order = 50)]
Init,
#[command(display_order = 51)]
Db {
#[command(subcommand)]
action: DbAction,
},
#[command(display_order = 52)]
Completion {
shell: Shell,
},
}
#[derive(ValueEnum, Clone, Debug)]
pub enum Shell {
Bash,
Zsh,
Fish,
PowerShell,
Elvish,
}
#[derive(Subcommand)]
pub enum DbAction {
Status,
Rebuild,
}
#[derive(Subcommand)]
pub enum EventsAction {
Rebuild,
Validate,
}
#[derive(Subcommand)]
pub enum ProblemAction {
#[command(display_order = 0)]
New {
title: String,
#[arg(long, default_value = "medium")]
priority: String,
#[arg(long)]
parent: Option<String>,
#[arg(long)]
milestone: Option<String>,
#[arg(long, short = 'f')]
force: bool,
#[arg(long, value_delimiter = ',')]
tags: Vec<String>,
},
#[command(display_order = 1)]
List {
#[arg(long)]
status: Option<String>,
#[arg(long)]
tree: bool,
#[arg(long)]
milestone: Option<String>,
#[arg(long)]
search: Option<String>,
#[arg(long)]
assignee: Option<String>,
#[arg(long)]
tag: Option<String>,
#[arg(long, default_value = "priority")]
sort: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 2)]
Show {
problem_id: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 3)]
Edit {
problem_id: String,
#[arg(long)]
title: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
priority: Option<String>,
#[arg(long)]
parent: Option<String>,
#[arg(long)]
add_tag: Option<String>,
#[arg(long)]
remove_tag: Option<String>,
#[arg(long, value_delimiter = ',')]
set_tags: Option<Vec<String>>,
},
#[command(display_order = 4)]
Tree {
problem_id: Option<String>,
},
#[command(display_order = 5)]
Solve {
problem_id: String,
#[arg(long)]
github_close: bool,
},
#[command(display_order = 6)]
Dissolve {
problem_id: String,
#[arg(long)]
reason: Option<String>,
#[arg(long)]
github_close: bool,
},
#[command(display_order = 7)]
Assign {
problem_id: String,
#[arg(long)]
to: Option<String>,
},
#[command(display_order = 8)]
Reopen {
problem_id: String,
},
#[command(display_order = 9)]
Duplicate {
problem_id: String,
#[arg(long)]
of: String,
},
#[command(display_order = 10)]
Graph {
#[arg(long)]
milestone: Option<String>,
#[arg(long)]
all: bool,
},
}
#[derive(Subcommand)]
pub enum SolutionAction {
#[command(display_order = 0)]
New {
title: String,
#[arg(long)]
problem: Option<String>,
#[arg(long)]
supersedes: Option<String>,
#[arg(long, value_name = "REVIEWER")]
reviewer: Vec<String>,
#[arg(long, short = 'f')]
force: bool,
#[arg(long, value_delimiter = ',')]
tags: Vec<String>,
},
#[command(display_order = 1)]
List {
#[arg(long)]
problem: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
assignee: Option<String>,
#[arg(long)]
search: Option<String>,
#[arg(long)]
tag: Option<String>,
#[arg(long, default_value = "status")]
sort: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 2)]
Show {
solution_id: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 3)]
Edit {
solution_id: String,
#[arg(long)]
title: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
add_tag: Option<String>,
#[arg(long)]
remove_tag: Option<String>,
#[arg(long, value_delimiter = ',')]
set_tags: Option<Vec<String>>,
},
#[command(display_order = 4)]
Attach {
solution_id: String,
#[arg(long)]
force: bool,
},
#[command(display_order = 5)]
Detach {
solution_id: String,
change_id: Option<String>,
#[arg(long)]
force: bool,
},
#[command(display_order = 6)]
Submit {
solution_id: String,
},
#[command(display_order = 7)]
Approve {
solution_id: Option<String>,
#[arg(long)]
force: bool,
#[arg(long)]
rationale: Option<String>,
#[arg(long)]
no_rationale: bool,
},
#[command(display_order = 8)]
Withdraw {
solution_id: String,
#[arg(long)]
rationale: Option<String>,
#[arg(long)]
no_rationale: bool,
},
#[command(display_order = 9)]
Assign {
solution_id: String,
#[arg(long)]
to: Option<String>,
},
#[command(display_order = 10)]
Resume {
solution_id: String,
},
#[command(display_order = 11)]
Lgtm {
solution_id: String,
},
#[command(display_order = 12)]
Comment {
solution_id: Option<String>,
#[arg(long, short = 'c')]
critique: Option<String>,
body: Option<String>,
},
#[command(display_order = 13)]
Diff {
solution_id: String,
},
}
#[derive(Subcommand)]
pub enum CritiqueAction {
#[command(display_order = 0)]
New {
solution_id: String,
title: String,
#[arg(long, default_value = "medium")]
severity: String,
#[arg(long)]
file: Option<String>,
#[arg(long)]
line: Option<usize>,
#[arg(long)]
reviewer: Option<String>,
},
#[command(display_order = 1)]
List {
#[arg(long)]
solution: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
reviewer: Option<String>,
#[arg(long)]
author: Option<String>,
#[arg(long)]
mine: bool,
#[arg(long)]
search: Option<String>,
#[arg(long)]
json: bool,
},
#[command(display_order = 2)]
Show {
critique_id: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 3)]
Edit {
critique_id: String,
#[arg(long)]
title: Option<String>,
#[arg(long)]
severity: Option<String>,
#[arg(long)]
status: Option<String>,
},
#[command(display_order = 4)]
Address {
critique_id: String,
},
#[command(display_order = 5)]
Validate {
critique_id: String,
},
#[command(display_order = 6)]
Dismiss {
critique_id: String,
},
#[command(display_order = 7)]
Reply {
critique_id: String,
body: String,
},
}
#[derive(Subcommand)]
pub enum MilestoneAction {
#[command(display_order = 0)]
New {
title: String,
#[arg(long)]
date: Option<String>,
},
#[command(display_order = 1)]
List {
#[arg(long)]
json: bool,
},
#[command(display_order = 2)]
Show {
milestone_id: String,
#[arg(long)]
json: bool,
},
#[command(display_order = 3)]
Edit {
milestone_id: String,
#[arg(long)]
title: Option<String>,
#[arg(long)]
date: Option<String>,
#[arg(long)]
status: Option<String>,
},
#[command(display_order = 4)]
Roadmap {
#[arg(long)]
json: bool,
},
#[command(display_order = 5)]
AddProblem {
milestone_id: String,
problem_id: String,
},
#[command(display_order = 6)]
RemoveProblem {
milestone_id: String,
problem_id: String,
},
#[command(display_order = 7)]
Assign {
milestone_id: String,
#[arg(long)]
to: Option<String>,
},
#[command(display_order = 8)]
Status {
milestone_id: String,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
pub enum GitHubSyncAction {
#[command(display_order = 0)]
Import {
issue: Option<String>,
#[arg(long)]
all: bool,
#[arg(long)]
label: Option<String>,
},
#[command(display_order = 1)]
Pr {
solution_id: Option<String>,
#[arg(long, default_value = "main")]
base: String,
},
#[command(display_order = 2)]
Status,
#[command(display_order = 3)]
Merge {
solution_id: String,
},
#[command(display_order = 4)]
Close {
problem_id: String,
},
#[command(display_order = 5)]
Reopen {
problem_id: String,
},
#[command(display_order = 6)]
Push,
}
#[derive(Subcommand, Debug)]
pub enum RankAction {
Show {
milestone: Option<String>,
#[arg(long)]
by_user: bool,
#[arg(long)]
json: bool,
},
}