1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#![allow(clippy::struct_excessive_bools)]
use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
/// atproto / standard.site subcommands
#[derive(Subcommand, Debug, Clone)]
pub enum AtprotoCommand {
/// Authenticate with your atproto PDS using app-password credentials.
///
/// Reads atproto.handle from marmite.yaml and `ATPROTO_APP_PASSWORD` env var.
/// If no publication exists, prints the config needed in marmite.yaml.
Auth,
/// Publish posts to atproto as site.standard.document records.
Publish {
/// Force re-publish all posts, ignoring change detection
#[arg(long, short)]
force: bool,
/// Preview what would be published without making any changes
#[arg(long, short = 'n')]
dry_run: bool,
},
}
/// Global CLI Subcommands
#[derive(Subcommand, Debug, Clone)]
pub enum CliSubcommand {
/// Manage atproto / standard.site integration
#[command(subcommand)]
Atproto(AtprotoCommand),
}
/// Command Line Argument Parser for Marmite CLI
#[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)]
pub struct Cli {
/// Input folder containing markdown files
pub input_folder: Option<PathBuf>,
/// Output folder to generate the site
/// [default: `input_folder/site`]
pub output_folder: Option<PathBuf>,
/// Verbosity level (0-4)
/// [default: 0 warn]
/// options: -v: info,-vv: debug,-vvv: trace,-vvvv: trace all
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Detect changes and rebuild the site automatically
#[arg(long, short)]
pub watch: bool,
/// Serve the site with a built-in HTTP server
#[arg(long)]
pub serve: bool,
/// Address to bind the server
#[arg(long, default_value = "0.0.0.0:8000", requires = "serve")]
pub bind: String,
/// Path to custom configuration file
#[arg(long, short, default_value = "marmite.yaml")]
pub config: String,
/// Print debug messages
/// Deprecated: Use -vv for debug messages
#[arg(long, hide = true)]
pub debug: bool,
/// Initialize templates in the project
#[arg(long)]
pub init_templates: bool,
/// Initialize a theme with templates and static assets
#[arg(long)]
pub start_theme: Option<String>,
/// Download and set a theme from a remote URL or local folder
#[arg(long)]
pub set_theme: Option<String>,
/// Generate the configuration file
#[arg(long)]
pub generate_config: bool,
/// Init a new site with sample content and default configuration
/// this will overwrite existing files
/// usually you don't need to run this because
/// Marmite can generate a site from any folder with markdown files
#[arg(long)]
pub init_site: bool,
/// Force the rebuild of the site even if no changes detected
#[arg(long)]
pub force: bool,
/// List all available shortcodes
#[arg(long)]
pub shortcodes: bool,
/// Show all site URLs organized by content type
#[arg(long)]
pub show_urls: bool,
/// Print the embedded agent skill document (SKILL.md) to stdout
#[arg(long)]
pub skill: bool,
/// Install the skill into .agents/skills/ in the current directory
#[arg(long)]
pub skill_install: bool,
/// Also install the skill into .claude/skills/ for Claude Code
#[arg(long)]
pub skill_install_claude: bool,
/// Create a new markdown file in the input folder
#[command(flatten)]
pub create: Create,
/// Override configuration values from CLI arguments
#[command(flatten)]
pub configuration: Configuration,
/// Global subcommand
#[command(subcommand)]
pub subcommand: Option<CliSubcommand>,
}
/// Create a new markdown file in the input folder
#[derive(Args, Debug, Clone)]
pub struct Create {
/// Create a new post with the given title and open in the default editor
#[arg(long)]
pub new: Option<String>,
/// Edit the file in the default editor
#[arg(short, requires = "new")]
pub edit: bool,
/// Set the new content as a page
#[arg(short, requires = "new")]
pub page: bool,
/// Set the tags for the new content
/// tags are comma separated
#[arg(short, requires = "new")]
pub tags: Option<String>,
/// Directory within the content folder to create the file
#[arg(short = 'd', requires = "new", conflicts_with = "translates")]
pub directory: Option<String>,
/// Language code (ISO 639-1) for the new content
#[arg(long, requires = "new")]
pub lang: Option<String>,
/// Slug of the original content this file translates
#[arg(long, requires_all = ["new", "lang"])]
pub translates: Option<String>,
/// Target site within a workspace
#[arg(long, requires = "new")]
pub site: Option<String>,
}
/// Gather configuration values from CLI arguments
#[derive(Args, Debug, Clone)]
pub struct Configuration {
/// Site name [default: "Home" or value from config file]
#[arg(long)]
pub name: Option<String>,
/// Site tagline [default: empty or value from config file]
#[arg(long)]
pub tagline: Option<String>,
/// Site url [default: empty or value from config file]
#[arg(long)]
pub url: Option<String>,
/// If protocol is missing in the URL setting, whether to use HTTPS or not [default: false]
#[arg(long)]
pub https: Option<bool>,
/// Site footer [default: from '_footer.md' or config file]
#[arg(long)]
pub footer: Option<String>,
/// Site main language 2 letter code [default: "en" or value from config file]
#[arg(long)]
pub language: Option<String>,
/// Number of posts per page [default: 10 or value from config file]
#[arg(long)]
pub pagination: Option<usize>,
/// Enable search [default: false or from config file]
#[arg(long)]
pub enable_search: Option<bool>,
/// Show matched text snippets in search results [default: false or from config file]
#[arg(long)]
pub search_show_matches: Option<bool>,
/// Number of match snippets to show per search result [default: 3 or from config file]
#[arg(long)]
pub search_match_count: Option<usize>,
/// Enable backlinks and related content for posts [default: true or from config file]
#[arg(long, default_value = None)]
pub enable_related_content: Option<bool>,
/// Path for content subfolder [default: "content" or value from config file]
/// this is the folder where markdown files are stored inside `input_folder`
/// no need to change this if your markdown files are in `input_folder` directly
#[arg(long)]
pub content_path: Option<String>,
/// Path for templates subfolder [default: "templates" or value from config file]
#[arg(long)]
pub templates_path: Option<String>,
/// Path for static subfolder [default: "static" or value from config file]
#[arg(long)]
pub static_path: Option<String>,
/// Path for media subfolder [default: "media" or value from config file]
/// this path is relative to the folder where your content files are
#[arg(long)]
pub media_path: Option<String>,
/// Default date format [default: "%b %e, %Y" or from config file]
/// see <https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html>
#[arg(long)]
pub default_date_format: Option<String>,
/// Name of the colorscheme to use [default: "default" or from config file]
/// see <https://marmite.blog/getting-started.html#colorschemes>
#[arg(long)]
pub colorscheme: Option<String>,
/// Show Table of Contents in posts [default: false or from config file]
/// this will generate a table of contents for each post
#[arg(long)]
pub toc: Option<bool>,
/// Generate JSON Feed [default: false or from config file]
#[arg(long)]
pub json_feed: Option<bool>,
/// Show next and previous links in posts [default: true or from config file]
#[arg(long)]
pub show_next_prev_links: Option<bool>,
/// Publish markdown source files alongside HTML [default: false or from config file]
#[arg(long)]
pub publish_md: Option<bool>,
/// Source repository URL to link to markdown files [default: None or from config file]
#[arg(long)]
pub source_repository: Option<String>,
/// Image provider for automatic banner image download [default: None or from config file]
/// Available providers: picsum
#[arg(long)]
pub image_provider: Option<String>,
/// Theme to use for the site [default: from config file or embedded templates]
#[arg(long)]
pub theme: Option<String>,
/// Generate sitemap.xml file [default: true or from config file]
#[arg(long)]
pub build_sitemap: Option<bool>,
/// Generate urls.json file [default: true or from config file]
#[arg(long)]
pub publish_urls_json: Option<bool>,
/// Enable shortcodes processing [default: true or from config file]
#[arg(long)]
pub enable_shortcodes: Option<bool>,
/// Custom shortcode pattern (regex) [default: <!-- \.(\w+)(?:\s+([^-][\s\S]*?))?\s*--> or from config file]
#[arg(long)]
pub shortcode_pattern: Option<String>,
/// Skip image resizing during build [default: false or from config file]
/// Use this for faster development builds when image optimization is not needed
#[arg(long)]
pub skip_image_resize: Option<bool>,
/// Enable the development toolbar when serving [default: true or from config file]
#[arg(long)]
pub enable_toolbar: Option<bool>,
/// Check internal links during build [default: false or from config file]
#[arg(long)]
pub check_internal_links: Option<bool>,
/// Fail the build on broken internal links [default: false or from config file]
#[arg(long)]
pub strict_internal_links: Option<bool>,
/// Check media file links during build [default: false or from config file]
#[arg(long)]
pub check_media_links: Option<bool>,
/// Render mermaid diagrams to SVG at build time [default: true or from config file]
#[arg(long)]
pub native_mermaid_render: Option<bool>,
}