# gemcms Help

`gemcms` is a file-based CLI CMS for Gemini capsules. Most commands are run
inside a capsule directory, or one of its subdirectories. A capsule is detected
by the presence of `capsule.toml`.

## Global Usage

```text
gemcms
gemcms help
gemcms -h
gemcms --help
```

With no arguments, or with `help`, `-h`, or `--help`, `gemcms` prints the short
usage summary.

Command flags can be placed before or after positional arguments:

```bash
gemcms add ~/article.md --section journal
gemcms add --section journal ~/article.md
```

## Commands Overview

```text
gemcms add <file-or-directory> [options]
gemcms create capsule <name> [options]
gemcms create site <name> [options]
gemcms create page <slug> [options]
gemcms create article <title> [options]
gemcms create section <name> [options]
gemcms create menu <name>
gemcms create widget <type> [options]
gemcms menu add <menu> <kind> [target] [options]
gemcms list <pages|articles|menus|widgets>
gemcms status
gemcms health
gemcms build
gemcms check
```

`create capsule` and `create site` are aliases.

## `gemcms add`

Import an existing Markdown or Gemtext file, or a directory containing importable
files.

```text
gemcms add <file-or-directory> [--type article|page] [--title title] [--slug slug] [--section name] [--tags a,b] [--draft] [--menu name]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--type article|page` | `article` | Imported content type. Frontmatter `type` is used when present and this option is omitted. |
| `--title title` | inferred | Content title. Only valid when importing one file. |
| `--slug slug` | inferred | Content slug. Only valid when importing one file. |
| `--section name` | `articles` | Article section. Ignored for pages. |
| `--tags a,b` | none | Comma-separated tags. |
| `--draft` | `false` | Mark imported content as draft. Drafts are not built and are not added to menus. |
| `--menu name` | none | Add imported non-draft content to the named menu. |

Accepted input extensions:

```text
.md
.markdown
.gmi
.gemini
```

Directory imports recursively import supported files, skip hidden directories,
and sort files deterministically. `--title` and `--slug` cannot be used with
directory imports.

Title inference order:

1. Frontmatter `title`
2. First `# Heading`
3. File name converted to title case

Examples:

```bash
gemcms add ~/article.md --section journal --tags gemini --draft --menu top
gemcms add ~/notes/ --section journal --tags gemini
gemcms add ~/about.gmi --type page --slug about --menu top
```

## `gemcms create capsule`

Create a new capsule project. This command also creates the default top menu,
home page, recent-articles widget, content directories, asset directory, and
`public/`.

```text
gemcms create capsule <name> [--title title] [--host host] [--author name] [--language code] [--import path] [--type article|page] [--section name] [--tags a,b] [--draft] [--menu name] [--build]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--title title` | title-cased name | Capsule title written to `capsule.toml`. |
| `--host host` | empty | Gemini host name. |
| `--author name` | empty | Default author metadata. |
| `--language code` | `en` | Content language metadata. |
| `--import path` | none | Import one file or a directory after capsule creation. |
| `--type article|page` | inferred or `article` | Type for imported content. |
| `--section name` | `articles` | Section for imported articles. |
| `--tags a,b` | none | Tags for imported content. |
| `--draft` | `false` | Mark imported content as draft. |
| `--menu name` | none | Add imported non-draft content to a menu. |
| `--build` | `false` | Build `public/` after creation and optional import. |

Example:

```bash
gemcms create capsule my-capsule --title "My Capsule" --host gemini.example.org --import ~/articles --section journal --tags gemini --build
```

## `gemcms create site`

Alias of `create capsule`.

```text
gemcms create site <name> [same options as create capsule]
```

## `gemcms create page`

Create a new page in `content/pages/`.

```text
gemcms create page <slug> [--title title] [--menu name]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--title title` | title-cased slug | Page title. |
| `--menu name` | none | Add the page to the named menu. |

Example:

```bash
gemcms create page about --title "About" --menu top
```

## `gemcms create article`

Create a new article in `content/articles/`.

```text
gemcms create article <title> [--section name] [--tags a,b] [--draft] [--menu name]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--section name` | `articles` | Article section. |
| `--tags a,b` | none | Comma-separated tags. |
| `--draft` | `false` | Mark article as draft. Drafts are not built and are not added to menus. |
| `--menu name` | none | Add the non-draft article to the named menu. |

Example:

```bash
gemcms create article "Hello Gemini" --section journal --tags gemini,capsule --menu top
```

## `gemcms create section`

Create a section config in `site/sections/`.

```text
gemcms create section <name> [--title title]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--title title` | title-cased name | Section display title. |

Example:

```bash
gemcms create section journal --title "Journal"
```

## `gemcms create menu`

Create an empty menu config in `site/menus/`.

```text
gemcms create menu <name>
```

Example:

```bash
gemcms create menu footer
```

## `gemcms create widget`

Create a build-time widget config in `site/widgets/`.

```text
gemcms create widget <type> [--name name] [--section name] [--limit n]
```

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--name name` | widget type | Widget config name. |
| `--section name` | `articles` | Section used by recent article widgets. |
| `--limit n` | `5` | Maximum number of items. Values below `1` become `5`. |

Currently supported widget type:

| Type | Description |
| --- | --- |
| `recent` | Render recent non-draft articles from one section. |

Example:

```bash
gemcms create widget recent --name recent-journal --section journal --limit 10
```

Use the widget in content:

```text
{{ widget recent-journal }}
```

## `gemcms menu add`

Add an item to a menu. Missing menus are created automatically.

```text
gemcms menu add <menu> <home|page|section|article|custom> [target] [--label label] [--url url]
```

Kinds:

| Kind | Target required | Default URL | Notes |
| --- | --- | --- | --- |
| `home` | no | `/` | Default label: `Home`. |
| `page` | yes | `/<target>.gmi` | `index` maps to `/`. |
| `section` | yes | `/<target>/` | Links to a generated section index. |
| `article` | yes | `/articles/<target>.gmi` | Uses the default `articles` section path. |
| `custom` | yes | none | Requires `--url`. |

Options:

| Option | Default | Description |
| --- | --- | --- |
| `--label label` | inferred | Link label. |
| `--url url` | inferred | Link URL. Required for `custom`. |

Examples:

```bash
gemcms menu add top home
gemcms menu add top page about --label "About"
gemcms menu add top section journal --label "Journal"
gemcms menu add top custom external --label "Project" --url "gemini://example.org/"
```

Use the menu in content:

```text
{{ menu top }}
```

## `gemcms list`

List project objects.

```text
gemcms list <pages|articles|menus|widgets>
```

Output:

| Target | Output format |
| --- | --- |
| `pages` | `slug<TAB>title` |
| `articles` | `date<TAB>section<TAB>slug<TAB>title` |
| `menus` | `name<TAB>N items` |
| `widgets` | `name<TAB>type` |

Examples:

```bash
gemcms list pages
gemcms list articles
gemcms list menus
gemcms list widgets
```

## `gemcms status`

Print a project summary.

```text
gemcms status
```

Includes:

- capsule root
- title
- host
- page, article, and draft counts
- menu, section, and widget counts
- number of generated public files
- health summary

## `gemcms health`

Print only health status and issues.

```text
gemcms health
```

Returns an error exit when errors are present. Warnings are printed but do not
fail the command.

## `gemcms build`

Generate Gemtext output into `public/`.

```text
gemcms build
```

Build behavior:

- Deletes and recreates `public/`.
- Copies files from `assets/` into `public/`.
- Skips draft documents.
- Writes pages as `/<slug>.gmi`, with `index` mapped to `/index.gmi`.
- Writes articles as `/<section>/<slug>.gmi`.
- Generates `/index.gmi` if no `index` page exists.
- Generates section indexes as `/<section>/index.gmi`.
- Expands build-time blocks:
  - `{{ menu name }}`
  - `{{ widget name }}`

## `gemcms check`

Validate common capsule problems.

```text
gemcms check
```

Checks include:

- missing capsule title
- missing content documents
- missing index page warning
- missing slugs
- duplicate output paths
- broken or suspicious local Gemtext links
- empty menu labels or URLs
- missing widget type
- invalid recent widget section

The command prints `OK` when no issues are found. It returns an error exit when
errors are present.

## Capsule Layout

```text
capsule.toml
content/
  pages/
  articles/
assets/
site/
  menus/
  sections/
  widgets/
public/
```

## Content Frontmatter

Gemtext files use TOML-like frontmatter:

```text
+++
title = "Hello Gemini"
type = "article"
slug = "hello-gemini"
date = "2026-07-07"
section = "articles"
tags = ["gemini", "capsule"]
draft = true
+++

# Hello Gemini
```

Imported Markdown can also use simple YAML-style frontmatter:

```text
---
title: Hello Gemini
tags: gemini, capsule
---

# Hello Gemini
```

Markdown imports are converted to Gemtext before being stored.
