1pub mod commands;
3pub mod helpers;
4pub mod json_output;
5pub mod output;
6
7#[derive(Debug)]
12pub struct UserError(pub String);
13
14impl std::fmt::Display for UserError {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 f.write_str(&self.0)
17 }
18}
19
20impl std::error::Error for UserError {}
21
22use clap::Subcommand;
23use color_eyre::eyre::{Result, eyre};
24use commands::JournalArgs;
25use commands::note_ops::NoteSubcommand;
26use commands::workspace::WorkspaceSubcommand;
27use helpers::{
28 create_and_init_vault, load_and_resolve_workspace, load_settings, resolve_inbox_path,
29 resolve_quick_note_path,
30};
31use kimun_core::{NoteVault, VaultConfig};
32use output::OutputFormat;
33
34#[derive(Subcommand)]
35pub enum CliCommand {
36 Search {
38 query: String,
39 #[arg(long, value_enum, default_value = "text")]
40 format: OutputFormat,
41 },
42 Notes {
44 #[arg(long, help = "Filter notes by path prefix")]
45 path: Option<String>,
46 #[arg(long, value_enum, default_value = "text")]
47 format: OutputFormat,
48 },
49 Workspace {
51 #[command(subcommand)]
52 subcommand: WorkspaceSubcommand,
53 },
54 Note {
56 #[command(subcommand)]
57 subcommand: NoteSubcommand,
58 },
59 Journal(JournalArgs),
61 Doctor,
63 Mcp,
65 Labels {
67 #[arg(long, value_enum, default_value = "text")]
68 format: OutputFormat,
69 },
70 Update {
72 #[arg(long)]
74 check: bool,
75 },
76}
77
78pub async fn run_cli(command: CliCommand, config_path: Option<std::path::PathBuf>) -> Result<()> {
79 match command {
80 CliCommand::Workspace { subcommand } => {
81 let mut settings = load_settings(config_path)?;
82 commands::workspace::run(subcommand, &mut settings).await
83 }
84 CliCommand::Note { subcommand } => {
85 let (settings, workspace_path, workspace_name) =
86 load_and_resolve_workspace(config_path)?;
87 let quick_note_path = resolve_quick_note_path(&settings);
88 let inbox_path = resolve_inbox_path(&settings);
89 let cache_path = settings.index_for(&workspace_name);
90 let mut vault = NoteVault::new(
91 VaultConfig::new(workspace_path)
92 .with_index(cache_path)
93 .with_backup(true),
94 )
95 .await?;
96 vault.set_inbox_path(kimun_core::nfs::VaultPath::new(&inbox_path));
97 let result = if vault.index_ready() {
104 commands::note_ops::run(subcommand, &vault, &quick_note_path, &workspace_name).await
105 } else {
106 Err(eyre!(
107 "Workspace index is not ready.\nRun `kimun workspace reindex` to initialise it."
108 ))
109 };
110 vault.close().await;
111 result
112 }
113 CliCommand::Search { query, format } => {
114 let (vault, workspace_name) = create_and_init_vault(config_path).await?;
115 let result =
116 commands::search::run(&vault, &query, format, &workspace_name, false).await;
117 vault.close().await;
118 result
119 }
120 CliCommand::Notes { path, format } => {
121 let (vault, workspace_name) = create_and_init_vault(config_path).await?;
122 let result =
123 commands::notes::run(&vault, path.as_deref(), format, &workspace_name, false).await;
124 vault.close().await;
125 result
126 }
127 CliCommand::Journal(args) => {
128 let (vault, workspace_name) = create_and_init_vault(config_path).await?;
129 let result = commands::journal::run(args, &vault, &workspace_name).await;
130 vault.close().await;
131 result
132 }
133 CliCommand::Doctor => commands::doctor::run(config_path),
137 CliCommand::Mcp => commands::mcp::run(config_path).await,
138 CliCommand::Labels { format } => {
139 let (vault, workspace_name) = create_and_init_vault(config_path).await?;
140 let result = commands::labels::run(&vault, format, &workspace_name).await;
141 vault.close().await;
142 result
143 }
144 CliCommand::Update { check } => commands::update::run(check).await,
147 }
148}