1mod aliases;
2mod bundle;
3mod detect;
4mod explain;
5mod init;
6mod link;
7mod readers;
8mod render;
9mod search;
10mod share;
11mod sync;
12mod types;
13
14use anyhow::{Context, Result};
15use clap::{Parser, Subcommand};
16use std::path::PathBuf;
17use std::process::Command;
18
19#[derive(Parser)]
20#[command(name = "memex", about = "Self-managed context layer for coding agents")]
21struct Cli {
22 #[command(subcommand)]
23 command: Commands,
24}
25
26#[derive(Subcommand)]
27enum Commands {
28 Init,
30 Sync {
32 #[arg(long, default_value_t = 30)]
34 days: u64,
35 #[arg(long, default_value_t = false)]
37 quiet: bool,
38 },
39 LinkCommit {
41 #[arg(long, default_value_t = false)]
43 quiet: bool,
44 },
45 Explain {
47 commit: String,
49 },
50 Search {
52 query: String,
54 #[arg(long, default_value_t = 30)]
56 days: u64,
57 #[arg(long, default_value_t = 200)]
59 limit: usize,
60 #[arg(long, default_value_t = false)]
62 case_sensitive: bool,
63 #[arg(long, default_value_t = false)]
65 files: bool,
66 },
67 Share {
69 #[arg(long, conflicts_with = "passphrase_env")]
71 passphrase: Option<String>,
72 #[arg(long, value_name = "VAR", conflicts_with = "passphrase")]
74 passphrase_env: Option<String>,
75 },
76 ShareSession {
78 session: String,
80 #[arg(long)]
82 passphrase: Option<String>,
83 },
84 Import {
86 id: String,
88 #[arg(long)]
90 passphrase: Option<String>,
91 },
92 Unlock {
94 #[arg(long)]
96 passphrase: Option<String>,
97 },
98}
99
100pub fn run() -> Result<()> {
101 let cli = Cli::parse();
102 let repo_root = find_repo_root()?;
103
104 match cli.command {
105 Commands::Init => init::run_init(&repo_root),
106 Commands::Sync { days, quiet } => sync::run_sync(&repo_root, days, quiet),
107 Commands::LinkCommit { quiet } => link::run_link_commit(&repo_root, quiet),
108 Commands::Explain { commit } => explain::run_explain(&repo_root, &commit),
109 Commands::Search {
110 query,
111 days,
112 limit,
113 case_sensitive,
114 files,
115 } => search::run_search(&repo_root, &query, days, limit, case_sensitive, files),
116 Commands::Share {
117 passphrase,
118 passphrase_env,
119 } => share::run_share(&repo_root, passphrase, passphrase_env),
120 Commands::ShareSession {
121 session,
122 passphrase,
123 } => bundle::run_share_session(&repo_root, &session, passphrase),
124 Commands::Import { id, passphrase } => bundle::run_import(&repo_root, &id, passphrase),
125 Commands::Unlock { passphrase } => share::run_unlock(&repo_root, passphrase),
126 }
127}
128
129fn find_repo_root() -> Result<PathBuf> {
130 let output = Command::new("git")
131 .args(["rev-parse", "--show-toplevel"])
132 .output()
133 .context("failed to run git rev-parse")?;
134
135 if output.status.success() {
136 let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
137 Ok(PathBuf::from(path))
138 } else {
139 std::env::current_dir().context("failed to get current directory")
140 }
141}