Terminal-first notes CLI with SQLite storage and git sync.
Project description
Terminotes

Terminotes is a terminal-first note taking CLI, 99% vibecoded with Codex CLI and GPT-5 under my tight supervision 😉.
It focuses on fast capture from a shell, durable storage in SQLite, and simple Git synchronization so you can keep your notes database in a repo and carry it between machines.
Features
- Fast capture via editor (
tn edit) and direct log entries (tn log -- ...). - SQLite storage with simple schema and safe parameterized queries.
- Git-backed portability: store the DB in a repo and sync on demand.
- Practical commands: list, search, delete, info, sync, and export.
- Link capture with
tn link, including optional comments, auto-appliedlinktags, and Wayback fallbacks.
Requirements
- Python 3.13+
- uv for environment and workflow.
- Git installed and a reachable remote for your notes repo.
Installation
Install Terminotes globally via uv:
uv tool install terminotes
This places the tn console script on your PATH, so you can invoke Terminotes from any shell without activating a virtual environment. Use tn --help to explore the CLI. If you prefer a local development checkout, follow the contributing instructions later in this document.
Quick Start
- Create or edit your configuration file:
tn config
This bootstraps a TOML file (default ~/.config/terminotes/config.toml). Update it to point to your notes repo and editor. Minimal example:
git_remote_url = "git@github.com:you/terminotes-notes.git"
terminotes_dir = "notes-repo" # absolute or relative to the config dir
editor = "vim"
Important: git_remote_url is required. Terminotes ensures a local clone exists under terminotes_dir and stores the SQLite DB there.
- Capture a first note:
tn edit
- List your notes:
tn ls --limit 10
- Sync with the remote when ready:
tn sync
Usage
Below are the primary subcommands. Use tn --help and tn <cmd> --help for details.
-
config— Create/open the config file in your editor.- Example:
tn config
- Example:
-
edit— Create a new note or edit an existing one.- New note:
tn edit - Edit by id:
tn edit --id 42 - Edit last updated:
tn edit --last
- New note:
-
log— Quick log entry without opening an editor.- Example:
tn log --tag work --tag focus -- This is a log entry - Title is derived from the first sentence or line, truncated when long.
- Repeat
--tagto associate tags; tag names are normalized to lowercase.
- Example:
-
link— Save a URL with optional comment and Wayback fallback metadata.- Example:
tn link https://example.com "Great article" --tag reading - Multi-word comments become the first paragraph of the note body, followed by the Markdown link (and Wayback fallback when present).
- Automatically adds the
linktag alongside any additional--tagarguments and stores the latest archived snapshot (when available) under the note's extra data. - Fetches the page title and appends the hostname when possible; otherwise the raw URL becomes the note title. A warning is shown if no Wayback snapshot is available.
- Example:
-
ls— List most recent notes (by last edit time).- Example:
tn ls --limit 10 --tag work - Options:
--limit/-n,--reverse,--tag/--tag(filter by tag; repeatable)
- Example:
-
search— Simple case-insensitive substring search across title/body/description.- Example:
tn search python --tag personal - Options:
--limit/-n,--reverse,--tag/--tag
- Example:
-
delete— Delete a note by id.- Example:
tn delete --yes 42 - Uses a confirmation prompt unless
--yesis provided.
- Example:
-
prune— Remove unused tags and stale tag associations.- Example:
tn prune
- Example:
-
sync— Fetch, detect divergence, and push with the selected strategy.- Requires a clean working tree; commit or stash changes first.
- Divergence prompt choices:
local-wins,remote-wins, orabort.
-
export— Render notes to a static site or Markdown files.- HTML:
tn export --format html --dest ./site --site-title "My Notes" - Markdown:
tn export --format markdown --dest ./markdown - Outputs go into the destination directory, which is created if missing.
- HTML:
-
info— Show current repo path, totals, and last edited note.
Configuration
The config file is TOML and lives by default at ~/.config/terminotes/config.toml. Keys:
git_remote_url(string, required): your notes repo remote URL.terminotes_dir(string, optional): where the local repo lives; absolute or relative path. Default:notes-repounder the config directory.editor(string, optional): command to launch when editing notes viatn edit.
You can start from config/config.sample.toml.
Data Model
Notes are stored in an SQLite file named terminotes.sqlite3 under terminotes_dir. Schema (simplified):
id(INTEGER PRIMARY KEY)title(TEXT)body(TEXT)description(TEXT)created_at(TEXT, ISO 8601)updated_at(TEXT, ISO 8601)can_publish(INTEGER as boolean)extra_data(TEXT as JSON) for structured metadata, e.g. link source and Wayback fallback URLs captured bytn link.- Tags are stored in a normalized (lowercase) many-to-many table; each note can belong to multiple tags and vice versa.
Timestamps are stored in UTC.
Git Sync
Terminotes uses your local clone of the notes repository and commits SQLite changes locally during edit/log/delete/prune. Network interaction only happens during tn sync:
fetch --prunethen divergence detection.- If remote-ahead or diverged, you can pick:
remote-wins: hard reset toorigin/<branch>(replaces local DB with remote).local-wins: force-push with lease.abort: do nothing.
- If no upstream exists,
tn syncpushes and sets upstream.
In non-interactive sessions, prompts are disabled and an error message is shown with guidance. A clean working tree is required.
Development
Use uv and the provided Justfile tasks:
just bootstrap # uv sync + pre-commit install
just fmt # ruff format
just lint # ruff check
just test # pytest
just precommit # run all pre-commit tasks
Contributing
Pull requests are welcome. Before submitting:
- Follow Conventional Commits (e.g.,
feat(cli): add search subcommand). - Run
just precommit. - Include a summary, test output, and linked issues in your PR.
See AGENTS.md for repository conventions and tips.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file terminotes-0.6.2.tar.gz.
File metadata
- Download URL: terminotes-0.6.2.tar.gz
- Upload date:
- Size: 53.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ff82d7678bc2ecc40afa28383f50d8a504dd3865053ade9311b344e679ef639
|
|
| MD5 |
518a979bfaa65c97f7f7c02eddf3c69c
|
|
| BLAKE2b-256 |
b4a78445e4dbfc34a87466787d89d572bb9dcc8faf5dee0487f055fc71c49972
|
File details
Details for the file terminotes-0.6.2-py3-none-any.whl.
File metadata
- Download URL: terminotes-0.6.2-py3-none-any.whl
- Upload date:
- Size: 33.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc89754dcb8fe7540334212c8dc4e42dfdfeb43b4e52cf6053299e423cd56d5c
|
|
| MD5 |
cd8dd24930c242b10cd4b9c995a8cf08
|
|
| BLAKE2b-256 |
526c1ce08e078c73b30871ef71cfc541fbff7f9c6cf5333506fb5da204d0d37e
|