personal activity index (bluesky, leaflet, substack)
pai.desertthunder.dev
rss
bluesky
1use clap::{Parser, Subcommand};
2use pai_core::SourceKind;
3use std::path::PathBuf;
4
5/// Personal Activity Index - POSIX-style CLI for content aggregation
6#[derive(Parser, Debug)]
7#[command(name = "pai")]
8#[command(version, about, long_about = None)]
9pub struct Cli {
10 /// Set configuration directory
11 #[arg(short = 'C', value_name = "DIR", global = true)]
12 pub config_dir: Option<PathBuf>,
13
14 /// Path to SQLite database file
15 #[arg(short = 'd', value_name = "PATH", global = true)]
16 pub db_path: Option<PathBuf>,
17
18 #[command(subcommand)]
19 pub command: Commands,
20}
21
22#[derive(Parser, Debug)]
23pub struct ExportOpts {
24 /// Filter by source kind
25 #[arg(short = 'k', value_name = "KIND")]
26 pub kind: Option<SourceKind>,
27
28 /// Filter by specific source ID
29 #[arg(short = 'S', value_name = "ID")]
30 pub source_id: Option<String>,
31
32 /// Maximum number of items
33 #[arg(short = 'n', value_name = "NUMBER")]
34 pub limit: Option<usize>,
35
36 /// Only items published at or after this time
37 #[arg(short = 's', value_name = "TIME")]
38 pub since: Option<String>,
39
40 /// Filter items by substring
41 #[arg(short = 'q', value_name = "PATTERN")]
42 pub query: Option<String>,
43
44 /// Output format
45 #[arg(short = 'f', value_name = "FORMAT", default_value = "json")]
46 pub format: String,
47
48 /// Output file (default: stdout)
49 #[arg(short = 'o', value_name = "FILE")]
50 pub output: Option<PathBuf>,
51}
52
53#[derive(Subcommand, Debug)]
54pub enum Commands {
55 /// Fetch and store content from configured sources
56 Sync {
57 /// Sync all configured sources (default)
58 #[arg(short = 'a')]
59 all: bool,
60
61 /// Sync only a particular source kind
62 #[arg(short = 'k', value_name = "KIND")]
63 kind: Option<SourceKind>,
64
65 /// Sync only a specific source instance
66 #[arg(short = 'S', value_name = "ID")]
67 source_id: Option<String>,
68 },
69
70 /// Inspect stored items
71 List {
72 /// Filter by source kind
73 #[arg(short = 'k', value_name = "KIND")]
74 kind: Option<SourceKind>,
75
76 /// Filter by specific source ID
77 #[arg(short = 'S', value_name = "ID")]
78 source_id: Option<String>,
79
80 /// Maximum number of items to display
81 #[arg(short = 'n', value_name = "NUMBER", default_value = "20")]
82 limit: usize,
83
84 /// Only show items published at or after this time
85 #[arg(short = 's', value_name = "TIME")]
86 since: Option<String>,
87
88 /// Filter items by substring in title/summary
89 #[arg(short = 'q', value_name = "PATTERN")]
90 query: Option<String>,
91 },
92
93 /// Produce feeds or export files
94 Export(ExportOpts),
95
96 /// Self-host HTTP API
97 Serve {
98 /// Address to bind HTTP server to
99 #[arg(short = 'a', value_name = "ADDRESS", default_value = "127.0.0.1:8080")]
100 address: String,
101 },
102
103 /// Verify database schema and print statistics
104 DbCheck,
105
106 /// Initialize configuration file
107 Init {
108 /// Force overwrite existing config
109 #[arg(short = 'f')]
110 force: bool,
111 },
112
113 /// Generate or install the pai(1) manpage
114 Man {
115 /// Output file (default: stdout)
116 #[arg(short = 'o', value_name = "FILE")]
117 output: Option<PathBuf>,
118
119 /// Install into a manpath directory (defaults to ~/.local/share/man if unset)
120 #[arg(long)]
121 install: bool,
122
123 /// Custom directory for --install (e.g., /usr/local/share/man)
124 #[arg(long, value_name = "DIR")]
125 install_dir: Option<PathBuf>,
126 },
127
128 /// Initialize Cloudflare Worker deployment scaffolding
129 #[command(name = "cf-init")]
130 CfInit {
131 /// Output directory for scaffolding (default: current directory)
132 #[arg(short = 'o', value_name = "DIR")]
133 output_dir: Option<PathBuf>,
134
135 /// Dry run - show what would be created without writing files
136 #[arg(long)]
137 dry_run: bool,
138 },
139}