mod commands;
mod editor;
mod git;
mod models;
mod store;
use anyhow::Result;
use clap::{Parser, Subcommand};
use commands::context::OutputFormat;
use models::NodeStatus;
#[derive(Parser)]
#[command(
name = "memex",
about = "Organize development work into a versioned, navigable DAG of conversation nodes",
version
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Init,
Node {
#[command(subcommand)]
subcommand: NodeCommands,
},
Graph {
#[command(subcommand)]
subcommand: GraphCommands,
},
Context {
id: Option<String>,
#[arg(long, default_value = "markdown")]
format: String,
#[arg(long, default_value_t = 2)]
depth: usize,
},
Search {
query: String,
},
}
#[derive(Subcommand)]
enum NodeCommands {
Create {
#[arg(long)]
parent: Option<String>,
#[arg(long, name = "git-ref")]
git_ref: Option<String>,
#[arg(long, name = "tag", num_args = 1)]
tags: Vec<String>,
#[arg(long)]
goal: Option<String>,
},
Edit {
id: Option<String>,
#[arg(long)]
summary: Option<String>,
#[arg(long)]
goal: Option<String>,
#[arg(long, num_args = 1, action = clap::ArgAction::Append)]
decision: Vec<String>,
#[arg(long, num_args = 1, action = clap::ArgAction::Append)]
artifact: Vec<String>,
#[arg(long = "open-thread", num_args = 1, action = clap::ArgAction::Append)]
open_thread: Vec<String>,
#[arg(long, num_args = 1, action = clap::ArgAction::Append)]
rejected: Vec<String>,
},
Show {
id: Option<String>,
},
List,
Resolve {
id: Option<String>,
#[arg(long, short = 'y')]
force: bool,
},
Abandon {
id: Option<String>,
#[arg(long, short = 'y')]
force: bool,
},
Reopen {
id: Option<String>,
},
}
#[derive(Subcommand)]
enum GraphCommands {
View,
}
fn main() {
let cli = Cli::parse();
if let Err(e) = run(cli) {
eprintln!("Error: {:#}", e);
std::process::exit(1);
}
}
fn run(cli: Cli) -> Result<()> {
match cli.command {
Commands::Init => commands::init::run(),
Commands::Node { subcommand } => match subcommand {
NodeCommands::Create {
parent,
git_ref,
tags,
goal,
} => commands::node::create(
parent.as_deref(),
git_ref.as_deref(),
&tags,
goal.as_deref(),
),
NodeCommands::Edit {
id,
summary,
goal,
decision,
artifact,
open_thread,
rejected,
} => commands::node::edit(
id.as_deref(),
summary.as_deref(),
goal.as_deref(),
&decision,
&artifact,
&open_thread,
&rejected,
),
NodeCommands::Show { id } => commands::node::show(id.as_deref()),
NodeCommands::List => commands::node::list(),
NodeCommands::Resolve { id, force } => {
commands::node::set_status(id.as_deref(), NodeStatus::Resolved, force)
}
NodeCommands::Abandon { id, force } => {
commands::node::set_status(id.as_deref(), NodeStatus::Abandoned, force)
}
NodeCommands::Reopen { id } => {
commands::node::set_status(id.as_deref(), NodeStatus::Active, true)
}
},
Commands::Graph { subcommand } => match subcommand {
GraphCommands::View => commands::graph::view(),
},
Commands::Context { id, format, depth } => {
let fmt = OutputFormat::from_str(&format)?;
commands::context::run(id.as_deref(), fmt, depth)
}
Commands::Search { query } => commands::search::run(&query),
}
}