Terminal-native billable ledger for solo developers
Project description
TTD
Terminal-native billable ledger for solo developers who invoice by the hour.
Install
PyPI distribution: ttd-ledger. CLI command: ttd.
uv tool install ttd-ledger
ttd --help
One-off: uvx ttd-ledger.
Quick start (development)
just setup
prek run --all-files
uv run ttd
Development
| Command | Purpose |
|---|---|
just check |
Ruff lint/format check + ty (fast gate before PR or agent handoff) |
just setup |
Install dependencies (uv sync) and git hooks (uv run prek install) |
uv sync |
Install dependencies only |
prek install |
Install git hooks only |
prek run --all-files |
Run all checks (same as CI) |
uv run ttd |
CLI (Rich tables; health check by default) |
just db-seed |
Seed local DB with demo clients, projects, and entries |
just release-smoke |
Build wheel/sdist and verify ttd from the artifact |
just release |
Full checks + smoke, then trigger GitHub Release workflow |
uv run ttd-api |
Litestar API (scaffold) |
uv run ttd-tui |
Textual TUI (scaffold) |
Period export
Close a billing period to CSV (default), XLSX, or Numbers. Format is inferred from --output; omit it to print CSV to stdout.
# CSV to stdout
ttd export --from 2026-05-01 --to 2026-05-31
# CSV, XLSX, or Numbers file
ttd export --from 5/1 --to 5/31 --output period.csv
ttd export --from 5/1 --to 5/31 --client Acme --output period.xlsx
ttd export --from 5/1 --to 5/31 --output period.numbers
# Optional export-time round-up (minutes); set on client or project
ttd client add Acme --rate 150 --rounding-minutes 15
ttd project update --client Acme --name Website --rounding-minutes 30
Detail rows roll up duration entries by project, day, and note; interval entries stay one row each. A summary section (same file or Summary sheet) totals billable hours and hourly dollars by project and client. See docs/design/data-layer.md for column schemas.
Configuration
Persistent settings live in layered TOML files plus optional TTD_* env overrides. Inspect effective values and source layers with ttd config show; set local or global keys with ttd config set.
ttd config show
ttd config init
ttd config set data_dir ~/.local/share/ttd
ttd config set --global clock_format 24h
ttd config get db_filename
Global file: ~/.config/ttd/ttd.toml. Local override: nearest ttd.toml walking up from cwd. Precedence: env → local → global → defaults. See docs/design/data-layer.md.
Release (maintainers)
One-time setup
-
PyPI project — Register ttd-ledger (name must match
project.nameinpyproject.toml). -
Trusted publishing — On that project: Publishing → Add a new pending publisher → GitHub:
Field Value Owner syn54xRepository ttdWorkflow name release.ymlEnvironment pypi -
GitHub environment — Repo Settings → Environments → create
pypi(no secrets required for OIDC). -
GitHub Pages — Settings → Pages → Source: GitHub Actions.
Pre-release check
just release-smoke
Builds the wheel/sdist and runs ttd --help from the artifact (same layout CI publishes).
Publish
-
Complete one-time setup above.
-
Merge to
mainwith conventional commits since the last tag. -
From a clean, pushed
main:just releaseRuns CI checks,
release-smoke, then triggers the Release workflow (cz bump, PyPI publish, GitHub Release, docs deploy).Watch progress:
gh run watch --workflow release.yml
Failed publish recovery
If PyPI rejects the upload (for example filename reuse after a deleted version), main may already have the bump commit and tag while PyPI has nothing:
-
Bump to a new version (PyPI never reuses deleted filenames).
-
Update
CHANGELOG.mdfor that version. -
Delete stray or failed tags (
git push origin :refs/tags/v0.x.y). -
Push
main, tag the recovery version, push the tag. -
Re-run the workflow with publish only (skips
cz bump):gh workflow run release.yml --ref main -f publish_only=true gh run watch --workflow release.yml
Also delete accidental non-semver tags (e.g. list) — they break git describe and commitizen.
Smoke test after publish
uvx ttd-ledger --help
uv tool install ttd-ledger
ttd
Roadmap
Milestone sequencing and v1 scope: docs/roadmap.md. Product direction: STRATEGY.md.
Architecture
ttd.core— async domain services and SQLite (ferro-orm)ttd.cli— cyclopts CLI adapterttd.api— Litestar API adapter (scaffold)ttd.tui— Textual TUI adapter (scaffold)
Domain logic lives only in ttd.core. Surfaces are thin adapters.
Project details
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 ttd_ledger-0.2.1.tar.gz.
File metadata
- Download URL: ttd_ledger-0.2.1.tar.gz
- Upload date:
- Size: 35.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea7cf1b638a2c293a5e00f886351cd1164c3b5fd4e7862307769cf868a9ad530
|
|
| MD5 |
f739b643e0c0d49365c136dc3f9e8b11
|
|
| BLAKE2b-256 |
b95ce2b761fac9fa8d623e1380b0bc6f0273e553a5efde36dc2d4b458a69525d
|
Provenance
The following attestation bundles were made for ttd_ledger-0.2.1.tar.gz:
Publisher:
release.yml on syn54x/ttd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ttd_ledger-0.2.1.tar.gz -
Subject digest:
ea7cf1b638a2c293a5e00f886351cd1164c3b5fd4e7862307769cf868a9ad530 - Sigstore transparency entry: 1688147178
- Sigstore integration time:
-
Permalink:
syn54x/ttd@558c4859ce5c7c8c4d28db16ed324717f3327a52 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/syn54x
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@558c4859ce5c7c8c4d28db16ed324717f3327a52 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file ttd_ledger-0.2.1-py3-none-any.whl.
File metadata
- Download URL: ttd_ledger-0.2.1-py3-none-any.whl
- Upload date:
- Size: 55.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cd7fe6d9b41743b8dd3e7f57903036fa251366acc6117276686a5f2998f2df5
|
|
| MD5 |
d6f56515cebad00a658be37fa59fd282
|
|
| BLAKE2b-256 |
c3986551f271356886747bd4678193fb77fb00be5d824b078128c1f2679dc295
|
Provenance
The following attestation bundles were made for ttd_ledger-0.2.1-py3-none-any.whl:
Publisher:
release.yml on syn54x/ttd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ttd_ledger-0.2.1-py3-none-any.whl -
Subject digest:
5cd7fe6d9b41743b8dd3e7f57903036fa251366acc6117276686a5f2998f2df5 - Sigstore transparency entry: 1688147218
- Sigstore integration time:
-
Permalink:
syn54x/ttd@558c4859ce5c7c8c4d28db16ed324717f3327a52 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/syn54x
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@558c4859ce5c7c8c4d28db16ed324717f3327a52 -
Trigger Event:
workflow_dispatch
-
Statement type: