`
+
+Creates a timestamped backup of the SQLite database in the specified directory. The backup filename follows the format `subtrack_YYYYMMDD_HHmmss.db`.
+
+If the destination does not exist or is not a directory, the command exits with an error. The backup will not overwrite existing files (exclusive create).
+
+### Examples
+
+```bash
+# Backup to current directory
+subtrack backup .
+
+# Backup to ~/backups
+subtrack backup ~/backups
+```
diff --git a/docs/src/routes/commands/+page.svelte b/docs/src/routes/commands/+page.svelte
deleted file mode 100644
index 8e40107..0000000
--- a/docs/src/routes/commands/+page.svelte
+++ /dev/null
@@ -1,223 +0,0 @@
-
-
-
- Commands — subtrack
-
-
-
-
-
Commands
-
- subtrack provides six commands. Most support both interactive and non-interactive modes.
-
-
-
-
-
list
-
- Lists all subscriptions in a formatted table. Subscriptions are grouped by
- currency by default, with a subtotal row per group.
-
-
-
-
-
- | Option |
- Description |
-
-
-
-
- -c, --currency <C> |
- Convert all prices to the given currency using live exchange rates |
-
-
-
-
-
Examples
-
# List all subscriptions (grouped by currency)
-subtrack list
-
-# Convert all prices to JPY
-subtrack list --currency JPY
-
-
- When --currency is used, all prices are converted to the target
- currency (fetched from open.er-api.com)
- and displayed as a single group with a grand total.
-
-
-
-
-
add
-
- Adds a new subscription. Without flags, prompts for all fields interactively.
- Providing all flags skips prompts entirely (useful for scripts).
-
-
-
-
-
- | Option |
- Description |
-
-
-
-
- --name <name> |
- Subscription name (max 100 characters) |
-
-
- --price <price> |
- Payment amount — integer, non-negative, max 99,999,999 |
-
-
- --currency <C> |
- Currency code. Supported: JPY, USD, EUR, GBP, AUD, CAD, KRW, CNY, SGD, HKD |
-
-
- --cycle <cycle> |
- Billing cycle. One of: weekly, bi-weekly, monthly, quarterly, semi-annual, yearly |
-
-
- --tags <tags> |
- Comma-separated tags (max 10 tags, each max 50 characters) |
-
-
-
-
-
Examples
-
# Interactive mode
-subtrack add
-
-# Fully non-interactive (skips confirmation)
-subtrack add \
- --name Spotify \
- --price 980 \
- --currency JPY \
- --cycle monthly \
- --tags music
-
-# Partial flags — missing fields are prompted
-subtrack add --name Netflix
-
-# Tags with existing tag autocomplete
-subtrack add --name "AWS" --price 50 --currency USD --cycle monthly
-
-
-
-
delete
-
- Shows an interactive checkbox list of all subscriptions. Select one or more
- to delete. Confirmation is required before deletion.
-
-
-
- ⚠ Note: The delete command is always interactive.
- There is no non-interactive mode.
-
-
-
Example
-
subtrack delete
-
-
-
-
payment [period]
-
- Calculates and displays how much you pay over a given billing period. All
- subscriptions are automatically converted to the target period based on their
- billing cycle.
-
-
-
The period argument defaults to monthly. Valid values:
-
-
-
- | Period |
- Alias |
-
-
-
- weekly | per week |
- bi-weekly | per two weeks |
- monthly | per month (default) |
- quarterly | per 3 months |
- semi-annual | per 6 months |
- yearly | per year |
-
-
-
-
-
-
- | Option |
- Description |
-
-
-
-
- -c, --currency <C> |
- Convert all prices to the given currency using live exchange rates |
-
-
-
-
-
Examples
-
# Monthly total (default)
-subtrack payment
-
-# Yearly total
-subtrack payment yearly
-
-# Weekly total in JPY
-subtrack payment weekly --currency JPY
-
-
- When --currency is used, the total is displayed as a single amount
- in the target currency. Without it, totals are grouped by currency.
-
-
- If exchange rates cannot be fetched (e.g. offline), the command falls back to
- per-currency display without conversion.
-
-
-
-
-
tags <taglist...>
-
- Filters and displays subscriptions that have all specified tags
- (AND logic).
-
-
-
Examples
-
# Subscriptions tagged with "music"
-subtrack tags music
-
-# Subscriptions tagged with both "music" AND "video"
-subtrack tags music video
-
-# Subscriptions tagged with "entertainment", "video", and "kids"
-subtrack tags entertainment video kids
-
-
-
-
backup <destination>
-
- Creates a timestamped backup of the SQLite database in the specified directory.
- The backup filename follows the format subtrack_YYYYMMDD_HHmmss.db.
-
-
-
- If the destination does not exist or is not a directory, the command exits with
- an error. The backup will not overwrite existing files (exclusive create).
-
-
-
Examples
-
# Backup to current directory
-subtrack backup .
-
-# Backup to ~/backups
-subtrack backup ~/backups
-
diff --git a/docs/src/routes/configuration/+page.md b/docs/src/routes/configuration/+page.md
new file mode 100644
index 0000000..c2a52b3
--- /dev/null
+++ b/docs/src/routes/configuration/+page.md
@@ -0,0 +1,49 @@
+---
+title: Configuration
+description: Environment variables and configuration options for subtrack.
+---
+
+subtrack follows a **zero-configuration** philosophy. It works out of the box with sensible defaults. The only configurable option is the database directory.
+
+## Environment variables
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `SUBSC_CLI_DB_DIR` | Override the database directory | `~/.config/subtrack` |
+
+### Example usage
+
+```bash
+# Use a custom directory for the database
+export SUBSC_CLI_DB_DIR=~/project/subtrack-data
+subtrack list
+
+# Or set it per-command
+SUBSC_CLI_DB_DIR=/tmp/test-db subtrack add \
+ --name Spotify \
+ --price 980 \
+ --currency JPY \
+ --cycle monthly
+```
+
+
+ 💡 Tip: Changing SUBSC_CLI_DB_DIR lets you maintain separate databases — useful for testing or multi-profile setups.
+
+
+## No config file
+
+subtrack does not use configuration files (`.subtrackrc`, `subtrack.json`, etc.). All settings are controlled via environment variables or CLI flags. This keeps the tool simple and predictable.
+
+## Currency & cycle choices
+
+### Supported currencies (10)
+
+```
+JPY USD EUR GBP AUD CAD KRW CNY SGD HKD
+```
+
+### Supported billing cycles (6)
+
+```
+weekly bi-weekly monthly quarterly semi-annual yearly
+```
diff --git a/docs/src/routes/configuration/+page.svelte b/docs/src/routes/configuration/+page.svelte
deleted file mode 100644
index ae2696d..0000000
--- a/docs/src/routes/configuration/+page.svelte
+++ /dev/null
@@ -1,65 +0,0 @@
-
- Configuration — subtrack
-
-
-
-
-
Configuration
-
- subtrack follows a zero-configuration philosophy. It works
- out of the box with sensible defaults. The only configurable option is
- the database directory.
-
-
-
Environment variables
-
-
-
-
- | Variable |
- Description |
- Default |
-
-
-
-
- SUBSC_CLI_DB_DIR |
- Override the database directory |
- ~/.config/subtrack |
-
-
-
-
-
Example usage
-
# Use a custom directory for the database
-export SUBSC_CLI_DB_DIR=~/project/subtrack-data
-subtrack list
-
-# Or set it per-command
-SUBSC_CLI_DB_DIR=/tmp/test-db subtrack add \
- --name Spotify \
- --price 980 \
- --currency JPY \
- --cycle monthly
-
-
- 💡 Tip: Changing SUBSC_CLI_DB_DIR lets you
- maintain separate databases — useful for testing or multi-profile setups.
-
-
-
No config file
-
- subtrack does not use configuration files (.subtrackrc,
- subtrack.json, etc.). All settings are controlled via
- environment variables or CLI flags. This keeps the tool simple and
- predictable.
-
-
-
Currency & cycle choices
-
-
Supported currencies (10)
-
JPY USD EUR GBP AUD CAD KRW CNY SGD HKD
-
-
Supported billing cycles (6)
-
weekly bi-weekly monthly quarterly semi-annual yearly
-
diff --git a/docs/src/routes/data/+page.md b/docs/src/routes/data/+page.md
new file mode 100644
index 0000000..aed5881
--- /dev/null
+++ b/docs/src/routes/data/+page.md
@@ -0,0 +1,74 @@
+---
+title: Data & Storage
+description: How subtrack stores data, database location, backup and restore.
+---
+
+## Database location
+
+All subscription data is stored in a single SQLite database file at:
+
+```
+~/.config/subtrack/subtrack.db
+```
+
+The database is created automatically on first use. No database server or configuration is required.
+
+## Database structure
+
+Three tables with a many-to-many relationship:
+
+```
+subscriptions
+├── id INTEGER PRIMARY KEY AUTOINCREMENT
+├── name TEXT NOT NULL
+├── price INTEGER NOT NULL
+├── currency TEXT NOT NULL
+└── cycle TEXT NOT NULL
+
+tags
+├── id INTEGER PRIMARY KEY AUTOINCREMENT
+└── name TEXT NOT NULL UNIQUE
+
+subscription_tags
+├── subscription_id INTEGER NOT NULL (FK → subscriptions.id)
+└── tag_id INTEGER NOT NULL (FK → tags.id)
+│ PRIMARY KEY (subscription_id, tag_id)
+```
+
+Deleting a subscription automatically removes its tag associations via `ON DELETE CASCADE`. Orphaned tags (with no subscriptions) are not automatically cleaned up.
+
+## Prices are stored as integers
+
+Prices are stored as whole numbers (integers) in the database. This avoids floating-point precision issues. For display, prices are formatted with the appropriate currency symbol and decimal places using `Intl.NumberFormat`.
+
+## Backup
+
+Use the `backup` command to create a timestamped copy of your database:
+
+```bash
+subtrack backup ~/backups
+# Creates: ~/backups/subtrack_20260617_143000.db
+```
+
+Backups use exclusive file creation, so they will never overwrite an existing file. See the [Commands](/commands) reference for full details.
+
+## Restore from backup
+
+To restore from a backup, simply copy the backup file to the database location:
+
+```bash
+# Stop subtrack (close all running instances)
+cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
+```
+
+Make sure subtrack is not running when you restore, as changes are written to the database file on every command.
+
+## Custom database directory
+
+Override the default `~/.config/subtrack` directory with the `SUBSC_CLI_DB_DIR` environment variable:
+
+```bash
+SUBSC_CLI_DB_DIR=/path/to/custom/dir subtrack list
+```
+
+See [Configuration](/configuration) for more details.
diff --git a/docs/src/routes/data/+page.svelte b/docs/src/routes/data/+page.svelte
deleted file mode 100644
index 9be5faa..0000000
--- a/docs/src/routes/data/+page.svelte
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
- Data & Storage — subtrack
-
-
-
-
-
Data & Storage
-
-
Database location
-
- All subscription data is stored in a single SQLite database file at:
-
-
~/.config/subtrack/subtrack.db
-
- The database is created automatically on first use. No database server or
- configuration is required.
-
-
-
Database structure
-
Three tables with a many-to-many relationship:
-
-
subscriptions
-├── id INTEGER PRIMARY KEY AUTOINCREMENT
-├── name TEXT NOT NULL
-├── price INTEGER NOT NULL
-├── currency TEXT NOT NULL
-└── cycle TEXT NOT NULL
-
-tags
-├── id INTEGER PRIMARY KEY AUTOINCREMENT
-└── name TEXT NOT NULL UNIQUE
-
-subscription_tags
-├── subscription_id INTEGER NOT NULL (FK → subscriptions.id)
-└── tag_id INTEGER NOT NULL (FK → tags.id)
-│ PRIMARY KEY (subscription_id, tag_id)
-
-
- Deleting a subscription automatically removes its tag associations via
- ON DELETE CASCADE. Orphaned tags (with no subscriptions) are
- not automatically cleaned up.
-
-
-
Prices are stored as integers
-
- Prices are stored as whole numbers (integers) in the database. This avoids
- floating-point precision issues. For display, prices are formatted with the
- appropriate currency symbol and decimal places using
- Intl.NumberFormat.
-
-
-
Backup
-
- Use the backup command to create a timestamped copy of your
- database:
-
-
subtrack backup ~/backups
-# Creates: ~/backups/subtrack_20260617_143000.db
-
- Backups use exclusive file creation, so they will never overwrite an existing
- file. See the Commands reference for full
- details.
-
-
-
Restore from backup
-
To restore from a backup, simply copy the backup file to the database location:
-
# Stop subtrack (close all running instances)
-cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
-
- Make sure subtrack is not running when you restore, as changes are written to
- the database file on every command.
-
-
-
Custom database directory
-
- Override the default ~/.config/subtrack directory with the
- SUBSC_CLI_DB_DIR environment variable:
-
-
SUBSC_CLI_DB_DIR=/path/to/custom/dir subtrack list
-
- See Configuration for more details.
-
-
diff --git a/docs/src/routes/development/+page.md b/docs/src/routes/development/+page.md
new file mode 100644
index 0000000..eb588a6
--- /dev/null
+++ b/docs/src/routes/development/+page.md
@@ -0,0 +1,78 @@
+---
+title: Development
+description: How to set up, build, test, and contribute to subtrack.
+---
+
+## Repository setup
+
+```bash
+git clone https://github.com/nazozokc/subtrack.git
+cd subtrack
+pnpm install
+```
+
+## Nix devShell
+
+If you use Nix, a devShell is available with all required tools:
+
+```bash
+nix develop
+```
+
+This provides `node`, `pnpm`, `typescript`, `typos`, and `nixfmt`.
+
+## Available commands
+
+| Command | Description |
+|---------|-------------|
+| `pnpm start` | Run subtrack directly from TypeScript source via `tsx` |
+| `pnpm build` | Build the CLI bundle to `dist/index.mjs` via `tsdown` |
+| `pnpm test` | Run tests with `vitest` |
+| `pnpm test:watch` | Run tests in watch mode |
+| `pnpm lint:typos` | Check for spelling errors with `typos` |
+
+## Tech stack
+
+| Category | Choice |
+|----------|--------|
+| Runtime | Node.js |
+| Language | TypeScript (strict mode, ESM) |
+| CLI framework | `commander` |
+| Interactive prompts | `@inquirer/prompts` |
+| Terminal output | `consola`, `picocolors`, `cli-table3` |
+| Database | `sql.js` (SQLite via WASM) |
+| Exchange rates | [open.er-api.com](https://open.er-api.com) |
+| Build tool | `tsdown` |
+| Test framework | `vitest` |
+| Package manager | `pnpm` |
+| Documentation | SvelteKit (this site) |
+
+## Contributing
+
+See [CONTRIBUTING.md](https://github.com/nazozokc/subtrack/blob/main/CONTRIBUTING.md) for branch policy, AI agent guidelines, and PR requirements.
+
+### Before opening a PR
+
+```bash
+pnpm build
+pnpm test
+```
+
+Make sure both pass and CI is green.
+
+## Project structure
+
+```
+subtrack/
+├── subtrack/ # CLI tool (TypeScript/ESM)
+│ ├── src/
+│ │ ├── index.ts # Entry point, commander setup
+│ │ ├── commands.ts # Command handlers
+│ │ ├── db.ts # SQLite database layer
+│ │ ├── display.ts # Table rendering, currency conversion
+│ │ └── prompts.ts # Input validation, prompt helpers
+│ └── dist/ # Built output
+├── docs/ # Documentation site (SvelteKit)
+├── flake.nix # Nix devShell
+└── pnpm-workspace.yaml
+```
diff --git a/docs/src/routes/development/+page.svelte b/docs/src/routes/development/+page.svelte
deleted file mode 100644
index 3919bfa..0000000
--- a/docs/src/routes/development/+page.svelte
+++ /dev/null
@@ -1,103 +0,0 @@
-
- Development — subtrack
-
-
-
-
-
Development
-
-
Repository setup
-
git clone https://github.com/nazozokc/subtrack.git
-cd subtrack
-pnpm install
-
-
Nix devShell
-
- If you use Nix, a devShell is available with all required tools:
-
-
nix develop
-
- This provides node, pnpm, typescript,
- typos, and nixfmt.
-
-
-
Available commands
-
-
-
- | Command |
- Description |
-
-
-
-
- pnpm start |
- Run subtrack directly from TypeScript source via tsx |
-
-
- pnpm build |
- Build the CLI bundle to dist/index.mjs via tsdown |
-
-
- pnpm test |
- Run tests with vitest |
-
-
- pnpm test:watch |
- Run tests in watch mode |
-
-
- pnpm lint:typos |
- Check for spelling errors with typos |
-
-
-
-
-
Tech stack
-
-
-
- | Category |
- Choice |
-
-
-
- | Runtime | Node.js |
- | Language | TypeScript (strict mode, ESM) |
- | CLI framework | commander |
- | Interactive prompts | @inquirer/prompts |
- | Terminal output | consola, picocolors, cli-table3 |
- | Database | sql.js (SQLite via WASM) |
- | Exchange rates | open.er-api.com |
- | Build tool | tsdown |
- | Test framework | vitest |
- | Package manager | pnpm |
- | Documentation | SvelteKit (this site) |
-
-
-
-
Contributing
-
- See CONTRIBUTING.md
- for branch policy, AI agent guidelines, and PR requirements.
-
-
-
Before opening a PR
-
pnpm build
-pnpm test
-
Make sure both pass and CI is green.
-
-
Project structure
-
subtrack/
-├── subtrack/ # CLI tool (TypeScript/ESM)
-│ ├── src/
-│ │ ├── index.ts # Entry point, commander setup
-│ │ ├── commands.ts # Command handlers
-│ │ ├── db.ts # SQLite database layer
-│ │ ├── display.ts # Table rendering, currency conversion
-│ │ └── prompts.ts # Input validation, prompt helpers
-│ └── dist/ # Built output
-├── docs/ # Documentation site (SvelteKit)
-├── flake.nix # Nix devShell
-└── pnpm-workspace.yaml
-
diff --git a/docs/src/routes/faq/+page.md b/docs/src/routes/faq/+page.md
new file mode 100644
index 0000000..c23cb19
--- /dev/null
+++ b/docs/src/routes/faq/+page.md
@@ -0,0 +1,66 @@
+---
+title: FAQ
+description: Frequently asked questions and troubleshooting for subtrack.
+---
+
+## I lost my database. Can I recover it?
+
+If you have a backup (created with `subtrack backup`), copy it back to `~/.config/subtrack/subtrack.db`. Without a backup, the data cannot be recovered — the database is stored locally only.
+
+**Recommendation:** Set up regular automated backups via cron or Task Scheduler. See the [Data & Storage](/data) page for details.
+
+## Can I add support for more currencies?
+
+The supported currencies are defined in `src/prompts.ts` as `CURRENCY_CHOICES`. To add a new currency:
+
+1. Add it to the `CURRENCY_CHOICES` array
+2. Add it to the `Currency` type in `src/db.ts`
+3. Verify that [open.er-api.com](https://open.er-api.com) supports the currency
+
+Pull requests for additional currencies are welcome!
+
+## Does subtrack work offline?
+
+Yes. Listing, adding, deleting, and filtering all work fully offline. The only feature that requires internet is `--currency` conversion, which fetches live exchange rates from [open.er-api.com](https://open.er-api.com).
+
+When offline, `--currency` falls back to per-currency display without conversion.
+
+## How do I restore from a backup?
+
+Copy the backup file to the database location:
+
+```bash
+cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
+```
+
+Ensure no subtrack processes are running during the restore. The database is flushed to disk after each command.
+
+## Is my data sent anywhere?
+
+**No.** All data is stored locally in a SQLite file on your machine. There are no accounts, no telemetry, and no cloud sync. The only external request subtrack makes is to [open.er-api.com](https://open.er-api.com) for currency exchange rates — and only when you use the `--currency` flag.
+
+## Can I use subtrack in Docker or CI?
+
+Yes. subtrack is a standard Node.js CLI tool and works in any environment with Node.js 18+. For Docker:
+
+```dockerfile
+FROM node:22-alpine
+RUN npm install -g subtrack
+CMD ["subtrack", "list"]
+```
+
+Use `SUBSC_CLI_DB_DIR` to control where the database is stored in containerized environments.
+
+## How do I update subtrack?
+
+```bash
+npm update -g subtrack
+```
+
+Or, if installed via pnpm:
+
+```bash
+pnpm update -g subtrack
+```
+
+Your database will be preserved — updates only affect the CLI code, not your data.
diff --git a/docs/src/routes/faq/+page.svelte b/docs/src/routes/faq/+page.svelte
deleted file mode 100644
index e79ebd6..0000000
--- a/docs/src/routes/faq/+page.svelte
+++ /dev/null
@@ -1,102 +0,0 @@
-
- FAQ — subtrack
-
-
-
-
-
FAQ & Troubleshooting
-
-
-
-
I lost my database. Can I recover it?
-
- If you have a backup (created with subtrack backup), copy it
- back to ~/.config/subtrack/subtrack.db. Without a backup, the
- data cannot be recovered — the database is stored locally only.
-
-
- Recommendation: Set up regular automated backups via cron
- or Task Scheduler. See the Data & Storage page
- for details.
-
-
-
-
-
Can I add support for more currencies?
-
- The supported currencies are defined in src/prompts.ts as
- CURRENCY_CHOICES. To add a new currency:
-
-
- - Add it to the
CURRENCY_CHOICES array
- - Add it to the
Currency type in src/db.ts
- - Verify that open.er-api.com supports the currency
-
-
- Pull requests for additional currencies are welcome!
-
-
-
-
-
Does subtrack work offline?
-
- Yes. Listing, adding, deleting, and filtering all work fully offline. The
- only feature that requires internet is --currency conversion,
- which fetches live exchange rates from open.er-api.com.
-
-
- When offline, --currency falls back to per-currency display
- without conversion.
-
-
-
-
-
How do I restore from a backup?
-
- Copy the backup file to the database location:
-
-
cp ~/backups/subtrack_20260617_143000.db ~/.config/subtrack/subtrack.db
-
- Ensure no subtrack processes are running during the restore. The database
- is flushed to disk after each command.
-
-
-
-
-
Is my data sent anywhere?
-
- No. All data is stored locally in a SQLite file on your
- machine. There are no accounts, no telemetry, and no cloud sync. The only
- external request subtrack makes is to
- open.er-api.com for currency exchange rates — and only when you use
- the --currency flag.
-
-
-
-
-
Can I use subtrack in Docker or CI?
-
- Yes. subtrack is a standard Node.js CLI tool and works in any environment
- with Node.js 18+. For Docker:
-
-
FROM node:22-alpine
-RUN npm install -g subtrack
-CMD ["subtrack", "list"]
-
- Use SUBSC_CLI_DB_DIR to control where the database is stored
- in containerized environments.
-
-
-
-
-
How do I update subtrack?
-
npm update -g subtrack
-
- Or, if installed via pnpm:
-
-
pnpm update -g subtrack
-
- Your database will be preserved — updates only affect the CLI code, not
- your data.
-
-
diff --git a/docs/src/routes/guides/+page.md b/docs/src/routes/guides/+page.md
new file mode 100644
index 0000000..8a22fb2
--- /dev/null
+++ b/docs/src/routes/guides/+page.md
@@ -0,0 +1,94 @@
+---
+title: Usage Guides
+description: Practical usage examples and workflows for subtrack.
+---
+
+Practical workflows for managing your subscriptions with subtrack.
+
+## First-time setup & adding subscriptions
+
+After installation, start by adding your subscriptions. The interactive mode guides you through each field:
+
+```bash
+subtrack add
+```
+
+You'll be prompted for: name, price, currency, billing cycle, and tags. Once added, verify with:
+
+```bash
+subtrack list
+```
+
+### Example: Adding a Netflix subscription
+
+```bash
+subtrack add \
+ --name "Netflix Premium" \
+ --price 1980 \
+ --currency JPY \
+ --cycle monthly \
+ --tags "video,entertainment"
+```
+
+## Tag-based organization
+
+Tags are a powerful way to categorize subscriptions. Examples:
+
+- **By category:** `music`, `video`, `cloud`, `productivity`
+- **By priority:** `essential`, `nice-to-have`
+- **By payment method:** `credit-card`, `paypal`
+- **By usage:** `personal`, `work`, `family`
+
+Filter by tags:
+
+```bash
+# Find all music-related subscriptions
+subtrack tags music
+
+# Find subscriptions used for both work and personal
+subtrack tags work personal
+```
+
+Tags use AND logic — only subscriptions matching **all** specified tags are shown.
+
+## Understanding your spending
+
+Use `subtrack payment` to see your total spending across different periods:
+
+```bash
+# How much per month?
+subtrack payment
+
+# How much per year?
+subtrack payment yearly
+
+# Weekly cost in USD
+subtrack payment weekly --currency USD
+```
+
+`payment` automatically converts all billing cycles to the target period. A yearly subscription will be divided into monthly cost, and a weekly subscription will be multiplied accordingly.
+
+## Managing multi-currency subscriptions
+
+If you have subscriptions in different currencies (e.g., JPY for local services and USD for international ones), subtrack handles this natively:
+
+```bash
+# See everything in your local currency
+subtrack list --currency JPY
+
+# Compare spending across currencies
+subtrack list
+```
+
+Without `--currency`, subscriptions are grouped by their original currency with per-group subtotals.
+
+## Regular backups
+
+Set up a cron job (or Task Scheduler on Windows) for automatic backups:
+
+```bash
+# Example cron: daily backup at 3 AM
+0 3 * * * subtrack backup ~/subtrack-backups
+```
+
+Backups are timestamped and will never overwrite previous files. See [Data & Storage](/data) for restore instructions.
diff --git a/docs/src/routes/guides/+page.svelte b/docs/src/routes/guides/+page.svelte
deleted file mode 100644
index b8d667c..0000000
--- a/docs/src/routes/guides/+page.svelte
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
- Usage Guides — subtrack
-
-
-
-
-
Usage Guides
-
- Practical workflows for managing your subscriptions with subtrack.
-
-
-
-
-
First-time setup & adding subscriptions
-
- After installation, start by adding your subscriptions. The interactive mode
- guides you through each field:
-
-
subtrack add
-
- You'll be prompted for: name, price, currency, billing cycle, and tags.
- Once added, verify with:
-
-
subtrack list
-
-
Example: Adding a Netflix subscription
-
subtrack add \
- --name "Netflix Premium" \
- --price 1980 \
- --currency JPY \
- --cycle monthly \
- --tags "video,entertainment"
-
-
-
-
Tag-based organization
-
- Tags are a powerful way to categorize subscriptions. Examples:
-
-
- - By category:
music, video, cloud, productivity
- - By priority:
essential, nice-to-have
- - By payment method:
credit-card, paypal
- - By usage:
personal, work, family
-
-
-
Filter by tags:
-
# Find all music-related subscriptions
-subtrack tags music
-
-# Find subscriptions used for both work and personal
-subtrack tags work personal
-
-
- Tags use AND logic — only subscriptions matching all specified
- tags are shown.
-
-
-
-
-
Understanding your spending
-
- Use subtrack payment to see your total spending across different
- periods:
-
-
# How much per month?
-subtrack payment
-
-# How much per year?
-subtrack payment yearly
-
-# Weekly cost in USD
-subtrack payment weekly --currency USD
-
- payment automatically converts all billing cycles to the target
- period. A yearly subscription will be divided into monthly cost, and a weekly
- subscription will be multiplied accordingly.
-
-
-
-
-
Managing multi-currency subscriptions
-
- If you have subscriptions in different currencies (e.g., JPY for local services
- and USD for international ones), subtrack handles this natively:
-
-
# See everything in your local currency
-subtrack list --currency JPY
-
-# Compare spending across currencies
-subtrack list
-
- Without --currency, subscriptions are grouped by their original
- currency with per-group subtotals.
-
-
-
-
-
Regular backups
-
- Set up a cron job (or Task Scheduler on Windows) for automatic backups:
-
-
# Example cron: daily backup at 3 AM
-0 3 * * * subtrack backup ~/subtrack-backups
-
- Backups are timestamped and will never overwrite previous files. See
- Data & Storage for restore instructions.
-
-
diff --git a/docs/src/routes/installation/+page.md b/docs/src/routes/installation/+page.md
new file mode 100644
index 0000000..6470417
--- /dev/null
+++ b/docs/src/routes/installation/+page.md
@@ -0,0 +1,62 @@
+---
+title: Installation
+description: Install subtrack via npm, pnpm, or build from source.
+---
+
+## Requirements
+
+**subtrack** requires **Node.js 18 or later**. It is a pure Node.js package and does not require any system-level dependencies.
+
+## Install via npm (recommended)
+
+```bash
+npm install -g subtrack
+```
+
+## Install via pnpm
+
+```bash
+pnpm add -g subtrack
+```
+
+## Install via bun
+
+```bash
+bun add -g subtrack
+```
+
+## Run with npx (no install)
+
+If you prefer not to install globally, use `npx`:
+
+```bash
+npx subtrack list
+```
+
+Note that `npx` will download the package on every first run, so global install is recommended for regular use.
+
+## Build from source
+
+Clone the repository and build locally:
+
+```bash
+git clone https://github.com/nazozokc/subtrack.git
+cd subtrack
+pnpm install
+pnpm build
+pnpm link --global
+```
+
+This links the `subtrack` command to your global `node_modules`. Re-run `pnpm link --global` after pulling updates.
+
+
+ 💡 Tip: Run subtrack --help after installation to verify everything works.
+
+
+## Verify installation
+
+```bash
+subtrack --help
+```
+
+You should see the list of available commands.
diff --git a/docs/src/routes/installation/+page.svelte b/docs/src/routes/installation/+page.svelte
deleted file mode 100644
index d074c08..0000000
--- a/docs/src/routes/installation/+page.svelte
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
- Installation — subtrack
-
-
-
-
-
Installation
-
-
Requirements
-
- subtrack requires Node.js 18 or later.
- It is a pure Node.js package and does not require any system-level dependencies.
-
-
-
Install via npm (recommended)
-
npm install -g subtrack
-
-
Install via pnpm
-
pnpm add -g subtrack
-
-
Install via bun
-
bun add -g subtrack
-
-
Run with npx (no install)
-
If you prefer not to install globally, use npx:
-
npx subtrack list
-
- Note that npx will download the package on every first run,
- so global install is recommended for regular use.
-
-
-
Build from source
-
Clone the repository and build locally:
-
git clone https://github.com/nazozokc/subtrack.git
-cd subtrack
-pnpm install
-pnpm build
-pnpm link --global
-
- This links the subtrack command to your global node_modules.
- Re-run pnpm link --global after pulling updates.
-
-
-
- 💡 Tip: Run subtrack --help after installation to verify everything works.
-
-
-
Verify installation
-
subtrack --help
-
You should see the list of available commands.
-
diff --git a/docs/src/routes/tui/+page.md b/docs/src/routes/tui/+page.md
new file mode 100644
index 0000000..5f728be
--- /dev/null
+++ b/docs/src/routes/tui/+page.md
@@ -0,0 +1,171 @@
+---
+title: Interactive TUI
+description: Walkthrough of subtrack's interactive terminal UI — prompts, autocomplete, confirmations, and behaviors.
+---
+
+subtrack offers both **interactive** and **non-interactive** modes. The interactive mode uses [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) to guide you through each operation with live validation, select menus, and autocomplete hints.
+
+| Command | Interactive | Non-interactive |
+|---------|-------------|-----------------|
+| `add` | ✅ (default) | ✅ (all flags provided) |
+| `delete` | ✅ (always) | ❌ |
+| `list` | ❌ | ✅ |
+| `tags` | ❌ | ✅ |
+| `payment` | ❌ | ✅ |
+| `backup` | ❌ | ✅ |
+
+## `subtrack add` — interactive walkthrough
+
+Running `subtrack add` without flags starts a step-by-step prompt session.
+
+### 1. Name
+
+```
+? subscription name Netflix
+```
+
+- Text input with live validation
+- Cannot be empty
+- Max 100 characters
+- Validation error on invalid input:
+
+```
+ⓧ Name cannot be empty
+```
+
+### 2. Price
+
+```
+? monthly payment amount 1980
+```
+
+- Numeric input
+- Validated as a non-negative integer
+- Max 99,999,999
+
+### 3. Currency
+
+```
+? currency (Use arrow keys)
+ JPY (日本円)
+❯ USD (US Dollar)
+ EUR (Euro)
+ GBP (British Pound)
+ AUD (Australian Dollar)
+ CAD (Canadian Dollar)
+ KRW (South Korean Won)
+ CNY (Chinese Yuan)
+ SGD (Singapore Dollar)
+ HKD (Hong Kong Dollar)
+```
+
+- Select from 10 supported currencies
+- Navigate with arrow keys, confirm with Enter
+
+### 4. Cycle
+
+```
+? cycle (Use arrow keys)
+ weekly
+ bi-weekly
+❯ monthly
+ quarterly
+ semi-annual
+ yearly
+```
+
+- Select from 6 billing cycles
+
+### 5. Tags
+
+```
+? tags existing: music, video
+```
+
+- Text input with **existing tags shown as hints**
+- If you have previously created tags, they appear as `existing: tag1, tag2` to remind you of available values
+- Comma-separated, max 10 tags, each max 50 characters
+- Can be left blank (press Enter to skip)
+
+### 6. Confirmation
+
+```
+? Save "Netflix" (¥1,980, monthly)? (Y/n)
+```
+
+- Shows a summary of the subscription
+- **Default is `Yes`** — pressing Enter saves immediately
+- Type `n` to cancel
+
+After confirmation:
+
+```
+✔ Added subscription: Netflix
+```
+
+### Full-flag mode (no prompts)
+
+When all flags are provided (`--name`, `--price`, `--currency`, `--cycle`, `--tags`), **no prompts appear** and the subscription is saved immediately without confirmation:
+
+```bash
+subtrack add \
+ --name Netflix \
+ --price 1980 \
+ --currency JPY \
+ --cycle monthly \
+ --tags "video,entertainment"
+```
+
+Useful for scripts and automation.
+
+### Partial-flag mode
+
+You can provide some flags and let the rest be prompted. For example:
+
+```bash
+subtrack add --name Netflix
+```
+
+This prompts for price, currency, cycle, and tags — and shows the confirmation dialog since some fields were interactive.
+
+## `subtrack delete` — interactive walkthrough
+
+`delete` is **always interactive**. There is no non-interactive mode.
+
+### 1. Select subscriptions
+
+```
+? select subscriptions to delete (Use arrow keys to move, space to select)
+❯◯ Netflix — ¥1,980/month [video, entertainment]
+ ◯ Spotify — ¥980/month [music]
+ ◯ AWS — $50/month [cloud]
+```
+
+- **Multi-select** with checkbox (space to toggle, enter to confirm)
+- Each subscription shows: `name — price/cycle [tags]`
+- If no subscriptions exist, shows "No subscriptions found" and exits
+
+### 2. Confirmation
+
+```
+? Delete 2 subscriptions? (Netflix, Spotify) (y/N)
+```
+
+- Shows count and names of selected subscriptions
+- **Default is `No`** — pressing Enter does **not** delete
+- Type `y` to confirm
+
+After deletion, each removed subscription is confirmed:
+
+```
+✔ Deleted: Netflix
+✔ Deleted: Spotify
+```
+
+## Tips
+
+- **`add` confirmation defaults to `Yes`** for quick saves, but you can cancel with `n`.
+- **`delete` confirmation defaults to `No`** to prevent accidental deletion.
+- Use **arrow keys** for select menus, **space** for checkboxes, **Enter** to confirm.
+- Tags from previous sessions appear as **hints** — use consistent tag names to get autocomplete-like suggestions.
+- Validation errors are shown inline in prompts; invalid flag values in non-interactive mode print an error and abort the command.
diff --git a/docs/svelte.config.js b/docs/svelte.config.js
index 61ab2e4..24a3c9c 100644
--- a/docs/svelte.config.js
+++ b/docs/svelte.config.js
@@ -1,7 +1,17 @@
import adapter from "@sveltejs/adapter-static"
+import { mdsvex } from "mdsvex"
+import path from "node:path"
+import { fileURLToPath } from "node:url"
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
/** @type {import("@sveltejs/kit").Config} */
const config = {
+ extensions: [".svelte", ".md"],
+ preprocess: mdsvex({
+ extensions: [".md"],
+ layout: path.resolve(__dirname, "src/lib/layouts/MarkdownLayout.svelte"),
+ }),
kit: {
adapter: adapter({
fallback: "index.html",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 189d96f..568dee0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -22,6 +22,10 @@ importers:
vite:
specifier: ^6.3.5
version: 6.4.3(@types/node@25.9.3)(tsx@4.22.4)
+ devDependencies:
+ mdsvex:
+ specifier: ^0.12.7
+ version: 0.12.7(svelte@5.56.3)
subtrack:
dependencies:
@@ -866,6 +870,9 @@ packages:
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
+ '@types/mdast@4.0.4':
+ resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+
'@types/node@25.9.3':
resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==}
@@ -875,6 +882,9 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+ '@types/unist@2.0.11':
+ resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
+
'@vitest/expect@4.1.8':
resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==}
@@ -1091,6 +1101,11 @@ packages:
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ mdsvex@0.12.7:
+ resolution: {integrity: sha512-gx4bReLCUvq+MPErHXYeyX+TEq1hsS2KfiZtEOMNTcbibSouFy8AHc5h04KbGCl+g5tLuo4/lbgRVYRnc7bJZw==}
+ peerDependencies:
+ svelte: ^3.56.0 || ^4.0.0 || ^5.0.0-next.120
+
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
@@ -1122,6 +1137,13 @@ packages:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
+ prism-svelte@0.4.7:
+ resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==}
+
+ prismjs@1.30.0:
+ resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
+ engines: {node: '>=6'}
+
quansync@1.0.0:
resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
@@ -1280,6 +1302,21 @@ packages:
undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
+ unist-util-is@4.1.0:
+ resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==}
+
+ unist-util-stringify-position@2.0.3:
+ resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
+
+ unist-util-visit-parents@3.1.1:
+ resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==}
+
+ unist-util-visit@2.0.3:
+ resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==}
+
+ vfile-message@2.0.4:
+ resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==}
+
vite@6.4.3:
resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -1914,6 +1951,10 @@ snapshots:
'@types/jsesc@2.5.1': {}
+ '@types/mdast@4.0.4':
+ dependencies:
+ '@types/unist': 2.0.11
+
'@types/node@25.9.3':
dependencies:
undici-types: 7.24.6
@@ -1925,6 +1966,8 @@ snapshots:
'@types/trusted-types@2.0.7': {}
+ '@types/unist@2.0.11': {}
+
'@vitest/expect@4.1.8':
dependencies:
'@standard-schema/spec': 1.1.0
@@ -2139,6 +2182,16 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ mdsvex@0.12.7(svelte@5.56.3):
+ dependencies:
+ '@types/mdast': 4.0.4
+ '@types/unist': 2.0.11
+ prism-svelte: 0.4.7
+ prismjs: 1.30.0
+ svelte: 5.56.3
+ unist-util-visit: 2.0.3
+ vfile-message: 2.0.4
+
mrmime@2.0.1: {}
mute-stream@3.0.0: {}
@@ -2159,6 +2212,10 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ prism-svelte@0.4.7: {}
+
+ prismjs@1.30.0: {}
+
quansync@1.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -2345,6 +2402,28 @@ snapshots:
undici-types@7.24.6: {}
+ unist-util-is@4.1.0: {}
+
+ unist-util-stringify-position@2.0.3:
+ dependencies:
+ '@types/unist': 2.0.11
+
+ unist-util-visit-parents@3.1.1:
+ dependencies:
+ '@types/unist': 2.0.11
+ unist-util-is: 4.1.0
+
+ unist-util-visit@2.0.3:
+ dependencies:
+ '@types/unist': 2.0.11
+ unist-util-is: 4.1.0
+ unist-util-visit-parents: 3.1.1
+
+ vfile-message@2.0.4:
+ dependencies:
+ '@types/unist': 2.0.11
+ unist-util-stringify-position: 2.0.3
+
vite@6.4.3(@types/node@25.9.3)(tsx@4.22.4):
dependencies:
esbuild: 0.25.12