Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# changelog

## Unreleased
- Redeploys update news in place via `cite.lock` (commit it/keep it safe).
- Friendlier `metadata.yml`: inline timeline events, typos reported by line, flexible dates and links.
- Login with hidden password prompt and auto-refreshed sessions.
- Fixed private Markdown uploaded to the public bucket, broken timeline events, and edits not triggering a rebuild.
- Fixed `cite login` artist creation being rejected by row-level security, and login using a different Supabase project than deploy.

## 0.1.0-beta.0
- Package renamed from `cite-cli` to `cite` (binary, installers, and repo `laezyAI/cite`)
- Fixed `upgrade`, archived-project restore, and staging dry-run writing to the local database
Expand Down
33 changes: 7 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ tokio = { version = "1.44", features = ["rt", "rt-multi-thread", "macros", "fs",
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
sha2 = "0.10"
percent-encoding = "2.3"
uuid = { version = "1.23", features = ["v4"] }
uuid = { version = "1.23", features = ["v4", "v5"] }
colored = "3.0"
thiserror = "2.0"
tracing = "0.1"
Expand All @@ -28,13 +28,12 @@ ratatui = { version = "0.30.2", features = ["crossterm"] }
crossterm = { version = "0.29", features = ["event-stream"] }
futures-util = { version = "0.3", default-features = false, features = ["std"] }
libsql = { version = "0.10.0-pre.4", features = ["libsql-sys"] }
symphonia = { version = "0.6", default-features = false, features = ["mp3", "wav", "flac", "vorbis", "isomp4", "aac", "alac", "pcm"] }
symphonia = { version = "0.6", default-features = false, features = ["mp3", "wav", "isomp4", "aac", "alac", "pcm"] }
imagesize = "0.15"

[dev-dependencies]
tempfile = "3.19"
httpmock = "0.7"
regex = "1.13.0"

[profile.dist]
inherits = "release"
Expand Down
97 changes: 66 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,32 @@ cargo build --release
```bash
cite init my-project
# edit metadata.yml and add content files
cite doctor --path my-project
cite build --path my-project
cite login
cite deploy --path my-project
cite doctor --path my-project # check everything, with hints
cite login # prints the artist ids you can deploy as
cite deploy --path my-project # validates, rebuilds if needed, publishes
```

`deploy` rebuilds changed sources itself and refuses to send anything `doctor` reports
as an error; `cite build` is only needed to inspect `build/content.json`.

## Commands

| Command | Description |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `init <name>` | Create project structure |
| `doctor` | Validate project, metadata, files, assets, config, content quality, and media; show project health and local analytics |
| `doctor` | Check the project, metadata, content, and media; show errors, warnings, and the last build and deploy |
| `build` | Compile project → `build/content.json` (incremental) |
| `deploy` | Upload bundle to Supabase with verification |
| `deploy --staging` | Deploy to local cite.db instead of Supabase |
| `clean` | Remove build artifacts and cache |
| `rollback <deployment-id>` | Roll back to a previous deployment |
| `login` | Authenticate with Supabase credentials |
| `deploy` | Validate, rebuild if needed, and publish news, podcasts, timelines and assets; re-deploying updates in place |
| `deploy --dry-run` | Preview which episodes would be created or updated, from `cite.lock`, without contacting Supabase |
| `clean` | Remove `build/` and the incremental build cache (keeps `cite.lock` and deploy records) |
| `rollback <deployment-id>` | Remove the news a deployment created (updated items keep their content) |
| `login` | Sign in to Supabase and list the artists you can deploy as |
| `upgrade` | Self-update CLI |
| `uninstall` | Remove CLI and local data |

> Global options (all commands): `--path <dir>`, `--json`, `--quiet`, `--verbose`, `--dry-run`
>
> Command-specific flags: `build --force`, `deploy --staging`, `login --email <email> --password <password>`
> Global options (all commands): `--path <dir>`, `--json`, `--quiet`, `--verbose`
>
> Command-specific flags: `build --force`, `deploy --dry-run`, `login --email <email>` (the password is asked for without echo, or read from `CITE_PASSWORD`)

## Interactive Terminal UI

Expand All @@ -69,7 +71,7 @@ Run `cite` with no arguments to enter the TUI:
| `PgUp` / `PgDn` | Fast-scroll analytics |
| `←` / `→` | Navigate commands |
| `Enter` | Execute command (confirms Y/N for deploy/rollback) / select project / expand item |
| Type | Enter arguments for the selected command in Details panel |
| Type | Enter arguments for the selected command in the Commands panel |
| `Ctrl+r` | Refresh project list |
| `Ctrl+e` | Open file editor picker (Projects panel) |
| `Ctrl+l` / `Ctrl+a` | Toggle local / archived projects (Projects panel) |
Expand All @@ -82,49 +84,82 @@ Run `cite` with no arguments to enter the TUI:

```
my-project/
├── cite.toml # Project manifest (name, language, artist_id)
├── cite.toml # Project manifest (name, artist_id, optional [backend])
├── metadata.yml # Podcast metadata
├── .gitignore # Ignores build/
├── .gitignore # Ignores build/ and .cite/
├── content/ # Markdown & BibTeX files
├── assets/
│ ├── audio/ # Podcast audio (optional)
│ └── image/ # Thumbnails (optional)
├── cite.lock # News id of each deployed episode (commit this)
├── .cite/ # Deploy records for rollback (gitignored)
└── build/ # Generated output (gitignored)
```

## Metadata Model

Each entry in `metadata.yml` becomes one episode:
Each entry in `metadata.yml` becomes one episode (one news item in Supabase):

```yaml
podcasts:
- title: "My Podcast"
file: content/my-article.md
source_url: "https://example.com"
category: "artificial intelligence"
category: "artificial intelligence" # must match a Supabase category (case-insensitive)
summary: "Up to 50 words shown in the app" # optional; defaults to the first 50 words of `file`
source_url: www.example.com/story # optional; https:// is added when missing
audio: assets/audio/episode.mp3 # optional
thumbnail: assets/image/thumb.jpg # optional
timeline: # optional; deployed in order as timeline_news rows
- content/my-article.bib # BibTeX citation file -> inline events
- 26 # existing news item id -> linked row
- title: "Bill passes the Senate" # an event written inline
date: May 22, 2025 # optional; see below for accepted forms
url: example.com/story # optional (`link` works too)
description: "What happened" # optional
- content/my-article.bib # BibTeX citation file -> one event per entry
- content/earlier-episode.md # another episode of this project -> linked row
- 26 # news item already published (any artist) -> linked row
```

- `title` (up to 500 characters), `file`, and `category` are required to deploy.
- `timeline` accepts at most one `.bib` file; entries are deployed in the order listed.
BibTeX dates come from `date` (e.g. `2025-05-22`) or `year` and `month`; links from
`url`, `link` or `doi`.
- Dates may be written `2025`, `2025-05`, `2025-05-22`, `2025/05/22`, `May 2025`,
`22 May 2025` or `May 22, 2025`; a missing month or day means the first.
- Links without a scheme (`www.example.com/story`) get `https://`. Anything that is still
not a web link (spaces, `ftp://`) is an error.
- Surrounding spaces are trimmed and empty optional values are ignored.
- A misspelled key is an error naming the key and line (e.g. ``unknown field `catgory` ``),
never silently ignored.
- Audio: mp3, wav, m4a or aac, up to 100 MB. Thumbnails: jpg, png, webp or gif, up to 5 MB.
- `cite doctor` checks all of the above; `cite deploy` runs the same checks first.

### Updating episodes

An episode is identified by its `file`. Titles, content, category, and assets can
change freely: `cite deploy` updates the same news item each time. Renaming the
Markdown file publishes a new item.

`cite deploy` records which news item each episode became in `cite.lock`. Commit it
so every machine updates the same items; delete a line to publish that episode as a
new item on the next deploy.

## Authentication

Credentials are stored at `~/.cite/credentials.toml` (via `login`) or read from
the `CITE_SUPABASE_URL` and `CITE_SUPABASE_API_KEY` environment variables.
cite finds the Supabase project in this order:

## Local Analytics
1. `[backend]` in `cite.toml` (`url`, `api_key`)
2. `CITE_SUPABASE_URL` and `CITE_SUPABASE_API_KEY` environment variables
3. `~/.cite/credentials.toml`, written the first time `cite login` asks for them

cite maintains a local database at `~/.cite/cite.db` for:
`cite login` signs in with your email and password and lists the artists you own;
use one of their ids as `artist_id` in `cite.toml`. If you have none, it offers to
create one. The session is refreshed automatically when it expires.

- Compiler cache (file hashes, UUID mappings)
- Build and deployment history
- Project and podcast statistics (word count, reading time, audio duration)
- Asset metadata and usage tracking
- Offline analytics — no network required
## Local Data

Override the database location with `CITE_DB_PATH`.
cite keeps build cache, build and deploy history, and a snapshot of each project's
last build in `~/.cite/cite.db` (override with `CITE_DB_PATH`). It powers the TUI
analytics and restoring archived projects, and works offline.

## Tests

Expand Down
Loading
Loading