kotsu-cli 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kotsu_cli-0.1.0/.github/workflows/verificar.yml → kotsu_cli-0.1.1/.github/workflows/check.yml +4 -4
- kotsu_cli-0.1.0/.github/workflows/publicar.yml → kotsu_cli-0.1.1/.github/workflows/publish.yml +8 -8
- {kotsu_cli-0.1.0 → kotsu_cli-0.1.1}/.gitignore +5 -3
- kotsu_cli-0.1.1/AGENTS.md +110 -0
- {kotsu_cli-0.1.0 → kotsu_cli-0.1.1}/PKG-INFO +65 -59
- {kotsu_cli-0.1.0 → kotsu_cli-0.1.1}/README.md +64 -57
- kotsu_cli-0.1.1/docs/images/SOURCES.md +5 -0
- kotsu_cli-0.1.0/docs/imagens/banner.en.svg → kotsu_cli-0.1.1/docs/images/banner.svg +4 -4
- kotsu_cli-0.1.0/docs/imagens/icones/lancar.svg → kotsu_cli-0.1.1/docs/images/icons/add-entry.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/dados.svg → kotsu_cli-0.1.1/docs/images/icons/data.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/todomes.svg → kotsu_cli-0.1.1/docs/images/icons/every-month.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/instalar.svg → kotsu_cli-0.1.1/docs/images/icons/install.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/parcelas.svg → kotsu_cli-0.1.1/docs/images/icons/installments.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/teclado.svg → kotsu_cli-0.1.1/docs/images/icons/keyboard.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/licenca.svg → kotsu_cli-0.1.1/docs/images/icons/license.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/privado.svg → kotsu_cli-0.1.1/docs/images/icons/private.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/estrutura.svg → kotsu_cli-0.1.1/docs/images/icons/structure.svg +1 -1
- kotsu_cli-0.1.0/docs/imagens/icones/temas.svg → kotsu_cli-0.1.1/docs/images/icons/themes.svg +1 -1
- kotsu_cli-0.1.0/instalar.ps1 → kotsu_cli-0.1.1/install.ps1 +12 -12
- kotsu_cli-0.1.0/instalar.sh → kotsu_cli-0.1.1/install.sh +10 -10
- {kotsu_cli-0.1.0 → kotsu_cli-0.1.1}/pyproject.toml +6 -7
- kotsu_cli-0.1.1/src/kotsu/__init__.py +3 -0
- kotsu_cli-0.1.1/src/kotsu/__main__.py +215 -0
- kotsu_cli-0.1.1/src/kotsu/domain/__init__.py +5 -0
- kotsu_cli-0.1.1/src/kotsu/domain/filter.py +63 -0
- kotsu_cli-0.1.1/src/kotsu/domain/installments.py +50 -0
- kotsu_cli-0.1.1/src/kotsu/domain/line.py +185 -0
- kotsu_cli-0.1.1/src/kotsu/domain/merge.py +509 -0
- kotsu_cli-0.1.1/src/kotsu/domain/models.py +95 -0
- kotsu_cli-0.1.1/src/kotsu/domain/money.py +10 -0
- kotsu_cli-0.1.1/src/kotsu/domain/month.py +57 -0
- kotsu_cli-0.1.1/src/kotsu/domain/recurrence.py +20 -0
- kotsu_cli-0.1.1/src/kotsu/domain/totals.py +17 -0
- kotsu_cli-0.1.1/src/kotsu/storage/__init__.py +4 -0
- kotsu_cli-0.1.1/src/kotsu/storage/backup.py +70 -0
- kotsu_cli-0.1.1/src/kotsu/storage/database.py +515 -0
- kotsu_cli-0.1.1/src/kotsu/storage/exchange.py +284 -0
- kotsu_cli-0.1.1/src/kotsu/storage/lock.py +139 -0
- kotsu_cli-0.1.0/src/money_cli/armazenamento/migracoes/0001_inicial.sql → kotsu_cli-0.1.1/src/kotsu/storage/migrations/0001_initial.sql +5 -4
- kotsu_cli-0.1.0/src/money_cli/armazenamento/migracoes/0002_recorrencia.sql → kotsu_cli-0.1.1/src/kotsu/storage/migrations/0002_recurrence.sql +3 -2
- kotsu_cli-0.1.0/src/money_cli/armazenamento/migracoes/0003_mesclagem.sql → kotsu_cli-0.1.1/src/kotsu/storage/migrations/0003_merge.sql +8 -8
- kotsu_cli-0.1.1/src/kotsu/storage/migrations/0004_english_names.sql +124 -0
- kotsu_cli-0.1.1/src/kotsu/storage/migrations/__init__.py +32 -0
- kotsu_cli-0.1.1/src/kotsu/storage/paths.py +89 -0
- kotsu_cli-0.1.1/src/kotsu/storage/preferences.py +41 -0
- kotsu_cli-0.1.1/src/kotsu/ui/__init__.py +1 -0
- kotsu_cli-0.1.1/src/kotsu/ui/app.py +759 -0
- kotsu_cli-0.1.1/src/kotsu/ui/colors.py +31 -0
- kotsu_cli-0.1.1/src/kotsu/ui/dialogs.py +148 -0
- kotsu_cli-0.1.1/src/kotsu/ui/themes.py +79 -0
- kotsu_cli-0.1.1/tests/conftest.py +16 -0
- kotsu_cli-0.1.1/tests/test_add_entry.py +96 -0
- kotsu_cli-0.1.1/tests/test_backup.py +103 -0
- kotsu_cli-0.1.1/tests/test_domain.py +81 -0
- kotsu_cli-0.1.1/tests/test_exchange.py +612 -0
- kotsu_cli-0.1.1/tests/test_layers.py +46 -0
- kotsu_cli-0.1.1/tests/test_line.py +160 -0
- kotsu_cli-0.1.1/tests/test_m2_domain.py +149 -0
- kotsu_cli-0.1.1/tests/test_performance_m2.py +77 -0
- kotsu_cli-0.1.1/tests/test_recurrence.py +180 -0
- kotsu_cli-0.1.1/tests/test_storage.py +213 -0
- kotsu_cli-0.1.1/tests/test_sync.py +167 -0
- kotsu_cli-0.1.1/tests/test_themes.py +107 -0
- kotsu_cli-0.1.1/tests/test_ui.py +66 -0
- kotsu_cli-0.1.1/tests/test_ui_add_entry.py +187 -0
- kotsu_cli-0.1.1/tests/test_ui_m2.py +238 -0
- kotsu_cli-0.1.1/tools/check.py +51 -0
- kotsu_cli-0.1.1/tools/check_install.py +103 -0
- kotsu_cli-0.1.1/tools/generate_vectors.py +602 -0
- kotsu_cli-0.1.1/tools/layers.py +118 -0
- kotsu_cli-0.1.1/vectors/dates.json +350 -0
- kotsu_cli-0.1.1/vectors/invalid.json +674 -0
- kotsu_cli-0.1.1/vectors/line.json +433 -0
- kotsu_cli-0.1.1/vectors/merge.json +1948 -0
- kotsu_cli-0.1.1/vectors/money.json +54 -0
- kotsu_cli-0.1.1/vectors/stamp.json +44 -0
- kotsu_cli-0.1.0/AGENTS.md +0 -91
- kotsu_cli-0.1.0/README.pt-BR.md +0 -226
- kotsu_cli-0.1.0/docs/imagens/FONTES.md +0 -5
- kotsu_cli-0.1.0/docs/imagens/banner.svg +0 -49
- kotsu_cli-0.1.0/ferramentas/camadas.py +0 -118
- kotsu_cli-0.1.0/ferramentas/gerar_vetores.py +0 -595
- kotsu_cli-0.1.0/ferramentas/testar_instalacao.py +0 -102
- kotsu_cli-0.1.0/ferramentas/verificar.py +0 -51
- kotsu_cli-0.1.0/src/money_cli/__init__.py +0 -3
- kotsu_cli-0.1.0/src/money_cli/__main__.py +0 -197
- kotsu_cli-0.1.0/src/money_cli/armazenamento/__init__.py +0 -4
- kotsu_cli-0.1.0/src/money_cli/armazenamento/backup.py +0 -70
- kotsu_cli-0.1.0/src/money_cli/armazenamento/banco.py +0 -525
- kotsu_cli-0.1.0/src/money_cli/armazenamento/caminhos.py +0 -72
- kotsu_cli-0.1.0/src/money_cli/armazenamento/migracoes/__init__.py +0 -32
- kotsu_cli-0.1.0/src/money_cli/armazenamento/preferencias.py +0 -31
- kotsu_cli-0.1.0/src/money_cli/armazenamento/trava.py +0 -123
- kotsu_cli-0.1.0/src/money_cli/armazenamento/troca.py +0 -285
- kotsu_cli-0.1.0/src/money_cli/dominio/__init__.py +0 -5
- kotsu_cli-0.1.0/src/money_cli/dominio/dinheiro.py +0 -10
- kotsu_cli-0.1.0/src/money_cli/dominio/filtro.py +0 -63
- kotsu_cli-0.1.0/src/money_cli/dominio/linha.py +0 -179
- kotsu_cli-0.1.0/src/money_cli/dominio/mes.py +0 -57
- kotsu_cli-0.1.0/src/money_cli/dominio/mescla.py +0 -440
- kotsu_cli-0.1.0/src/money_cli/dominio/parcelas.py +0 -52
- kotsu_cli-0.1.0/src/money_cli/dominio/recorrencia.py +0 -20
- kotsu_cli-0.1.0/src/money_cli/dominio/tipos.py +0 -95
- kotsu_cli-0.1.0/src/money_cli/dominio/totais.py +0 -17
- kotsu_cli-0.1.0/src/money_cli/tela/__init__.py +0 -1
- kotsu_cli-0.1.0/src/money_cli/tela/app.py +0 -756
- kotsu_cli-0.1.0/src/money_cli/tela/cores.py +0 -31
- kotsu_cli-0.1.0/src/money_cli/tela/perguntas.py +0 -144
- kotsu_cli-0.1.0/src/money_cli/tela/temas.py +0 -74
- kotsu_cli-0.1.0/tests/conftest.py +0 -16
- kotsu_cli-0.1.0/tests/test_armazenamento.py +0 -151
- kotsu_cli-0.1.0/tests/test_backup.py +0 -103
- kotsu_cli-0.1.0/tests/test_camadas.py +0 -46
- kotsu_cli-0.1.0/tests/test_desempenho_m2.py +0 -77
- kotsu_cli-0.1.0/tests/test_dominio.py +0 -81
- kotsu_cli-0.1.0/tests/test_lancar.py +0 -92
- kotsu_cli-0.1.0/tests/test_linha.py +0 -158
- kotsu_cli-0.1.0/tests/test_m2_dominio.py +0 -145
- kotsu_cli-0.1.0/tests/test_recorrencia.py +0 -185
- kotsu_cli-0.1.0/tests/test_sincronia.py +0 -133
- kotsu_cli-0.1.0/tests/test_tela.py +0 -68
- kotsu_cli-0.1.0/tests/test_tela_lancar.py +0 -183
- kotsu_cli-0.1.0/tests/test_tela_m2.py +0 -238
- kotsu_cli-0.1.0/tests/test_temas.py +0 -96
- kotsu_cli-0.1.0/tests/test_troca.py +0 -560
- kotsu_cli-0.1.0/vetores/carimbo.json +0 -44
- kotsu_cli-0.1.0/vetores/datas.json +0 -350
- kotsu_cli-0.1.0/vetores/dinheiro.json +0 -54
- kotsu_cli-0.1.0/vetores/invalidos.json +0 -674
- kotsu_cli-0.1.0/vetores/linha.json +0 -405
- kotsu_cli-0.1.0/vetores/mescla.json +0 -1948
- {kotsu_cli-0.1.0 → kotsu_cli-0.1.1}/LICENSE +0 -0
- /kotsu_cli-0.1.0/docs/imagens/divisor.svg → /kotsu_cli-0.1.1/docs/images/divider.svg +0 -0
- {kotsu_cli-0.1.0/docs/imagens/icones → kotsu_cli-0.1.1/docs/images/icons}/backup.svg +0 -0
- {kotsu_cli-0.1.0/docs/imagens/icones → kotsu_cli-0.1.1/docs/images/icons}/dev.svg +0 -0
- {kotsu_cli-0.1.0/docs/imagens → kotsu_cli-0.1.1/docs/images}/stack/claude.svg +0 -0
- {kotsu_cli-0.1.0/docs/imagens → kotsu_cli-0.1.1/docs/images}/stack/githubactions.svg +0 -0
- {kotsu_cli-0.1.0/docs/imagens → kotsu_cli-0.1.1/docs/images}/stack/python.svg +0 -0
kotsu_cli-0.1.0/.github/workflows/verificar.yml → kotsu_cli-0.1.1/.github/workflows/check.yml
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: check
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
pull_request:
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
|
-
|
|
8
|
+
check:
|
|
9
9
|
strategy:
|
|
10
10
|
fail-fast: false
|
|
11
11
|
matrix:
|
|
@@ -18,6 +18,6 @@ jobs:
|
|
|
18
18
|
with:
|
|
19
19
|
python-version: ${{ matrix.python }}
|
|
20
20
|
- run: python -m pip install -e ".[dev]"
|
|
21
|
-
- run: python
|
|
21
|
+
- run: python tools/check.py
|
|
22
22
|
- run: python -m pip install pipx
|
|
23
|
-
- run: python
|
|
23
|
+
- run: python tools/check_install.py
|
kotsu_cli-0.1.0/.github/workflows/publicar.yml → kotsu_cli-0.1.1/.github/workflows/publish.yml
RENAMED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: publish
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Publishes to PyPI when a v* tag (e.g. v0.1.0) is pushed. Uses Trusted Publishing: no token.
|
|
4
4
|
on:
|
|
5
5
|
push:
|
|
6
6
|
tags: ["v*"]
|
|
7
7
|
|
|
8
8
|
jobs:
|
|
9
|
-
|
|
9
|
+
build:
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
11
|
steps:
|
|
12
12
|
- uses: actions/checkout@v4
|
|
13
13
|
- uses: actions/setup-python@v5
|
|
14
14
|
with:
|
|
15
15
|
python-version: "3.12"
|
|
16
|
-
- name:
|
|
16
|
+
- name: Check the tag version
|
|
17
17
|
run: |
|
|
18
18
|
v=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
19
|
-
test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME !=
|
|
19
|
+
test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != version $v"; exit 1; }
|
|
20
20
|
- run: pip install -e ".[dev]" build
|
|
21
|
-
- run: python
|
|
21
|
+
- run: python tools/check.py
|
|
22
22
|
- run: python -m build
|
|
23
23
|
- uses: actions/upload-artifact@v4
|
|
24
24
|
with:
|
|
25
25
|
name: dist
|
|
26
26
|
path: dist/
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
needs:
|
|
28
|
+
publish:
|
|
29
|
+
needs: build
|
|
30
30
|
runs-on: ubuntu-latest
|
|
31
31
|
environment: pypi
|
|
32
32
|
permissions:
|
|
@@ -7,17 +7,19 @@ dist/
|
|
|
7
7
|
.venv/
|
|
8
8
|
venv/
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# Tools
|
|
11
11
|
.pytest_cache/
|
|
12
12
|
.ruff_cache/
|
|
13
13
|
.coverage
|
|
14
14
|
htmlcov/
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# Personal data: never version databases, backups or exports
|
|
17
17
|
*.sqlite
|
|
18
18
|
*.sqlite3
|
|
19
19
|
*.sqlite-*
|
|
20
20
|
*.sqlite3-*
|
|
21
|
+
*.mcv
|
|
22
|
+
*.mcp
|
|
21
23
|
*.db
|
|
22
24
|
*.db-journal
|
|
23
25
|
*.db-wal
|
|
@@ -26,7 +28,7 @@ backups/
|
|
|
26
28
|
*.csv
|
|
27
29
|
.env
|
|
28
30
|
|
|
29
|
-
#
|
|
31
|
+
# Editors and system
|
|
30
32
|
.vscode/
|
|
31
33
|
.idea/
|
|
32
34
|
.DS_Store
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guide for anyone changing this repository, person or agent. Read it before changing code.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
Kotsu: a terminal app (Python + Textual) for the month's income, expenses, categories,
|
|
8
|
+
accounts and installments. Main target: Windows Terminal, working the same on Linux.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
kotsu --data ./tmp # opens the app with a test folder
|
|
15
|
+
python tools/check.py --quick # before every commit
|
|
16
|
+
python tools/check.py # before every PR (same as CI)
|
|
17
|
+
python tools/layers.py # only the layers rule
|
|
18
|
+
python tools/check_install.py # pipx install; uses the network only to install
|
|
19
|
+
python tools/generate_vectors.py # rewrites vectors/*.json (Android cases)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Layers (the test fails if they are broken)
|
|
23
|
+
|
|
24
|
+
| Folder | May import | Must not |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `src/kotsu/domain/` | only the standard library without I/O | `storage`, `ui`, `sqlite3`, `os`, `pathlib`, `io`, Textual, Rich |
|
|
27
|
+
| `src/kotsu/storage/` | `domain` | `ui`, Textual, Rich |
|
|
28
|
+
| `src/kotsu/ui/` | `domain`, `storage` | `sqlite3` directly |
|
|
29
|
+
|
|
30
|
+
`src/kotsu/__main__.py` is the assembly point and may see everything.
|
|
31
|
+
Financial rules (totals, installments, quick-line parser) live in `domain/`; the UI only shows.
|
|
32
|
+
|
|
33
|
+
## Data rules
|
|
34
|
+
|
|
35
|
+
- Money is an `int` in cents, always positive; the sign comes from `kind` (income/expense).
|
|
36
|
+
Never `float`.
|
|
37
|
+
- Every entry has an account (D21): the domain and the database refuse an entry without one.
|
|
38
|
+
The category is optional.
|
|
39
|
+
- Dates are calendar dates (`datetime.date`), no time zone, stored as `YYYY-MM-DD`.
|
|
40
|
+
- Totals (income, expenses, balance) are never stored: SQL computes them per month.
|
|
41
|
+
- Month queries use the range `date >= start AND date < end` so the index is used.
|
|
42
|
+
Lists are paginated (`limit`, `offset`).
|
|
43
|
+
- On screen, a negative amount shows only in red, without a sign (`domain.money.format_cents`).
|
|
44
|
+
- Amounts are typed and shown in the Brazilian format (`1.234,56`) and dates as `dd/mm/yyyy`;
|
|
45
|
+
everything else (code, UI text, docs) is in English.
|
|
46
|
+
|
|
47
|
+
## Database and migrations
|
|
48
|
+
|
|
49
|
+
- Every write goes through `Database.transaction()`: all or nothing. The installments of a
|
|
50
|
+
purchase go in a single transaction.
|
|
51
|
+
- Schema changed? Create `src/kotsu/storage/migrations/NNNN_name.sql` with the next number.
|
|
52
|
+
Never edit a published migration. Before migrating an existing database, the app copies the
|
|
53
|
+
file to `money.before-vN.sqlite`.
|
|
54
|
+
- Test every new migration with a database at the previous version.
|
|
55
|
+
- Migrations 0001 to 0003 still use the original Portuguese table names; 0004 renames
|
|
56
|
+
everything to English (and `receita`/`despesa` to `income`/`expense`).
|
|
57
|
+
- Recurrence (D36): `recurring` category + `recurrence` table (series template and
|
|
58
|
+
`generated_until`). `Database.extend_recurrences(month)` generates the missing months and
|
|
59
|
+
only moves forward, so a deleted month does not come back. The UI calls it when drawing,
|
|
60
|
+
limited to today + 24 months.
|
|
61
|
+
- Backup is manual only (D35): key `b` copies the database to `backups/` with date and time in
|
|
62
|
+
the name, without overwriting, and keeps only the 5 most recent (pruned after publishing).
|
|
63
|
+
The app never backs up on its own. Test clock and failures with temporary data. In-memory
|
|
64
|
+
(`Database.in_memory()`) makes no backup.
|
|
65
|
+
|
|
66
|
+
## Exchange between devices (D39)
|
|
67
|
+
|
|
68
|
+
- Each device has its own database; data is joined through an exchange file
|
|
69
|
+
(`--export`, `--import`, `--verify`). The merge rule is pure and lives in
|
|
70
|
+
`domain/merge.py`; reading, writing and applying live in `storage/exchange.py`.
|
|
71
|
+
- Every write to `account`, `category`, `entry` and the `recurrence` template stamps
|
|
72
|
+
`changed_at` with `Database.stamp(c)` (one stamp per transaction). Editing an entry clears
|
|
73
|
+
`generated`. Deleting an entry writes the tombstone before the `DELETE`. Forgot the stamp?
|
|
74
|
+
The change loses to the other device on merge.
|
|
75
|
+
- Advancing `generated_until` does not stamp: the generated month uses the fixed `uid`
|
|
76
|
+
(`month_uid`) and the series template's stamp.
|
|
77
|
+
- A new field in a synced table goes into: the migration, `exchange.read_state`,
|
|
78
|
+
`exchange._apply`, `merge.validate`, the canonical form (changes the check code) and
|
|
79
|
+
`FORMAT` if old files stop working. Format 1 files (Portuguese keys) are translated by
|
|
80
|
+
`merge.upgrade_v1`.
|
|
81
|
+
- `vectors/*.json` are the cases shared with the Android app. Changed a domain rule
|
|
82
|
+
(quick line, dates, money, stamp, merge)? Add the case in `tools/generate_vectors.py`, run
|
|
83
|
+
it and review the JSON diff. The test fails if the files are out of date.
|
|
84
|
+
|
|
85
|
+
## Compatibility with the Portuguese versions
|
|
86
|
+
|
|
87
|
+
Installed copies from before the English rename keep working after updating:
|
|
88
|
+
|
|
89
|
+
- Old flags (`--dados`, `--pasta-dados`, `--pasta-dados-padrao`, `--forcar`, `--exportar`,
|
|
90
|
+
`--importar`, `--conferir`) are still accepted, hidden from `--help`.
|
|
91
|
+
- `MONEY_CLI_DADOS` still works next to `KOTSU_DATA`; the `money` command still exists.
|
|
92
|
+
- The local folder `money-cli` is renamed to `kotsu`; `pasta_dados` in `local.toml` is read.
|
|
93
|
+
- `tema.toml` is read and replaced by `theme.toml`; old theme names map to the new ones.
|
|
94
|
+
- `kotsu.trava` from a computer not updated yet still blocks; the new lock is `kotsu.lock`.
|
|
95
|
+
- Dialog keys from before (`s`, `t`, `p`) still work next to the English ones.
|
|
96
|
+
|
|
97
|
+
## How to work
|
|
98
|
+
|
|
99
|
+
- Everything in English: code, messages, UI text, documents and file names.
|
|
100
|
+
- New or changed product decision: describe it in the PR and update the README in the same PR.
|
|
101
|
+
- Nothing goes into `main` without the maintainer's approval. Work on a branch and open a PR.
|
|
102
|
+
- Tests use neither the network nor the real data folder (use `Database.in_memory()` or
|
|
103
|
+
`tmp_path`).
|
|
104
|
+
- The README and this file are updated in the same PR that changes what they describe.
|
|
105
|
+
- Fixed palette in `ui/colors.py`; change it little and on purpose.
|
|
106
|
+
- Never version databases, backups or exports (`.gitignore` blocks SQLite, `*.mcv`, `*.mcp`
|
|
107
|
+
and `*.csv`).
|
|
108
|
+
- Real install tests via pipx run outside the checkout on Windows/Linux CI; they need the
|
|
109
|
+
network only to install. Unit tests stay offline.
|
|
110
|
+
- Commits with the GitHub noreply e-mail, fully in English.
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: kotsu-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Cozy personal finance in your terminal: income, expenses and installments of the month on one screen.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Sera-DAI/kotsu
|
|
6
6
|
Project-URL: Issues, https://github.com/Sera-DAI/kotsu/issues
|
|
7
|
-
Project-URL: Português, https://github.com/Sera-DAI/kotsu/blob/main/README.pt-BR.md
|
|
8
7
|
Author: Sera-DAI
|
|
9
8
|
License-Expression: MIT
|
|
10
9
|
License-File: LICENSE
|
|
@@ -23,11 +22,9 @@ Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
|
|
25
24
|
<p align="center">
|
|
26
|
-
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
25
|
+
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/banner.svg" alt="Kotsu: cozy personal finance in your terminal" width="100%">
|
|
27
26
|
</p>
|
|
28
27
|
|
|
29
|
-
<p align="center"><b>English</b> · <a href="https://github.com/Sera-DAI/kotsu/blob/main/README.pt-BR.md">Português (Brasil)</a></p>
|
|
30
|
-
|
|
31
28
|
<p align="center">
|
|
32
29
|
<img src="https://img.shields.io/badge/python-3.11%2B-ff7fb8?style=flat-square&labelColor=1d1424&logo=python&logoColor=ffe9f4" alt="Python 3.11+">
|
|
33
30
|
<img src="https://img.shields.io/badge/terminal-Windows%20%C2%B7%20Linux-ff7fb8?style=flat-square&labelColor=1d1424&logo=windowsterminal&logoColor=ffe9f4" alt="Windows Terminal and Linux">
|
|
@@ -36,14 +33,14 @@ Description-Content-Type: text/markdown
|
|
|
36
33
|
</p>
|
|
37
34
|
|
|
38
35
|
<p align="center">
|
|
39
|
-
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
40
|
-
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
41
|
-
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
36
|
+
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/stack/python.svg" width="44" alt="Python" title="Python">
|
|
37
|
+
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/stack/githubactions.svg" width="44" alt="GitHub Actions" title="GitHub Actions">
|
|
38
|
+
<img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/stack/claude.svg" width="44" alt="Claude" title="Claude">
|
|
42
39
|
</p>
|
|
43
40
|
|
|
44
41
|
<p align="center"><sub>Built with Python, Textual, SQLite and Claude</sub></p>
|
|
45
42
|
|
|
46
|
-
<p align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
43
|
+
<p align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/divider.svg" width="420" alt=""></p>
|
|
47
44
|
|
|
48
45
|
# Kotsu
|
|
49
46
|
|
|
@@ -51,43 +48,42 @@ Description-Content-Type: text/markdown
|
|
|
51
48
|
|
|
52
49
|
It runs on Windows Terminal and Linux, works offline and keeps everything in a SQLite file on your computer. No account, no cloud, no telemetry, no ads.
|
|
53
50
|
|
|
54
|
-
> The app interface is in Brazilian Portuguese for now (labels such as *receitas* = income, *despesas* = expenses, *saldo* = balance). The commands and keys below work the same in any language.
|
|
55
|
-
|
|
56
51
|
## Contents
|
|
57
52
|
|
|
58
53
|
- [I. What it does](#i-what-it-does)
|
|
59
54
|
- [II. Install](#ii-install)
|
|
60
55
|
- [III. Use](#iii-use)
|
|
61
56
|
- [IV. Your data and backups](#iv-your-data-and-backups)
|
|
62
|
-
- [V.
|
|
63
|
-
- [VI.
|
|
64
|
-
- [VII.
|
|
57
|
+
- [V. Updating from a Portuguese version](#v-updating-from-a-portuguese-version)
|
|
58
|
+
- [VI. Under the hood](#vi-under-the-hood)
|
|
59
|
+
- [VII. Development](#vii-development)
|
|
60
|
+
- [VIII. License](#viii-license)
|
|
65
61
|
|
|
66
62
|
## I. What it does
|
|
67
63
|
|
|
68
64
|
<table>
|
|
69
65
|
<tr>
|
|
70
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
71
|
-
<td><b>Quick line</b><br><code>89,90
|
|
66
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/add-entry.svg" width="44" alt=""></td>
|
|
67
|
+
<td><b>Quick line</b><br><code>89,90 groceries #food @nubank</code> and Enter. No forms: the amount comes first, then account, category and date in any order.</td>
|
|
72
68
|
</tr>
|
|
73
69
|
<tr>
|
|
74
|
-
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
70
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/installments.svg" width="44" alt=""></td>
|
|
75
71
|
<td><b>One screen</b><br>Income, expenses and balance for the month, one line per account (each with its own color) and the list of entries. The ← / → arrows switch months.</td>
|
|
76
72
|
</tr>
|
|
77
73
|
<tr>
|
|
78
|
-
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
79
|
-
<td><b>Installments and "every month"</b><br><code>120
|
|
74
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/every-month.svg" width="44" alt=""></td>
|
|
75
|
+
<td><b>Installments and "every month"</b><br><code>120 fridge @nubank 10x</code> becomes 10 installments, one per month. Categories marked "every month" (rent, internet, salary) show up on their own, with a <code>↻</code>.</td>
|
|
80
76
|
</tr>
|
|
81
77
|
<tr>
|
|
82
|
-
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
83
|
-
<td><b>Two Cotton Candy themes</b><br>Night (default) and light, with hearts, a little face and "zzz…
|
|
78
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/themes.svg" width="44" alt=""></td>
|
|
79
|
+
<td><b>Two Cotton Candy themes</b><br>Night (default) and light, with hearts, a little face and "zzz… zeroed" when an account is empty. The <code>t</code> key switches and your choice is saved.</td>
|
|
84
80
|
</tr>
|
|
85
81
|
<tr>
|
|
86
|
-
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
82
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/keyboard.svg" width="44" alt=""></td>
|
|
87
83
|
<td><b>Keyboard first</b><br>Everything has a key, and the footer reminds you which. Filter by <code>@account</code> or <code>#category</code> with <code>/</code>.</td>
|
|
88
84
|
</tr>
|
|
89
85
|
<tr>
|
|
90
|
-
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
86
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/private.svg" width="44" alt=""></td>
|
|
91
87
|
<td><b>Truly private</b><br>Your data stays on your computer. Kotsu never sends anything over the network.</td>
|
|
92
88
|
</tr>
|
|
93
89
|
</table>
|
|
@@ -96,21 +92,21 @@ It runs on Windows Terminal and Linux, works offline and keeps everything in a S
|
|
|
96
92
|
|
|
97
93
|
<table>
|
|
98
94
|
<tr>
|
|
99
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
100
|
-
<td>Requires Python 3.11 or newer and <a href="https://pipx.pypa.io/">pipx</a
|
|
95
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/install.svg" width="44" alt=""></td>
|
|
96
|
+
<td>Requires Python 3.11 or newer and <a href="https://pipx.pypa.io/">pipx</a>.</td>
|
|
101
97
|
</tr>
|
|
102
98
|
</table>
|
|
103
99
|
|
|
104
100
|
Windows Terminal (PowerShell), in one line:
|
|
105
101
|
|
|
106
102
|
```powershell
|
|
107
|
-
irm https://raw.githubusercontent.com/Sera-DAI/kotsu/main/
|
|
103
|
+
irm https://raw.githubusercontent.com/Sera-DAI/kotsu/main/install.ps1 | iex
|
|
108
104
|
```
|
|
109
105
|
|
|
110
106
|
Linux/macOS (needs pipx; the script tells you if it is missing):
|
|
111
107
|
|
|
112
108
|
```bash
|
|
113
|
-
curl -fsSL https://raw.githubusercontent.com/Sera-DAI/kotsu/main/
|
|
109
|
+
curl -fsSL https://raw.githubusercontent.com/Sera-DAI/kotsu/main/install.sh | bash
|
|
114
110
|
```
|
|
115
111
|
|
|
116
112
|
Or step by step, without a script:
|
|
@@ -136,21 +132,21 @@ Reopen the terminal and run `kotsu`. To update: `pipx upgrade kotsu-cli` (or run
|
|
|
136
132
|
## III. Use
|
|
137
133
|
|
|
138
134
|
```text
|
|
139
|
-
☁
|
|
135
|
+
☁ Cotton Candy ☁ November 2026
|
|
140
136
|
|
|
141
|
-
♡
|
|
137
|
+
♡ income 3.500,00 ✧ expenses 432,90 ☆ balance 3.067,10 (◕‿◕)♡
|
|
142
138
|
|
|
143
|
-
♡ itaú
|
|
144
|
-
♡ nubank
|
|
139
|
+
♡ itaú ♥♥♥♥♥♥♥♥♥· income 3.500,00 expenses 210,50
|
|
140
|
+
♡ nubank ♥♥♥♥♥♥♥♥♥♥ income 0,00 expenses 222,40
|
|
145
141
|
|
|
146
|
-
|
|
147
|
-
08/11
|
|
148
|
-
07/11
|
|
142
|
+
Date Description Category Account Amount
|
|
143
|
+
08/11 bakery food nubank 12,50
|
|
144
|
+
07/11 groceries food nubank 89,90
|
|
149
145
|
```
|
|
150
146
|
|
|
151
147
|
| Key | Action |
|
|
152
148
|
|---|---|
|
|
153
|
-
| `l` | opens the quick line (Enter saves, Esc closes) |
|
|
149
|
+
| `l` | opens the quick line to log an entry (Enter saves, Esc closes) |
|
|
154
150
|
| ← / → | previous / next month |
|
|
155
151
|
| `/` | filters by `@account`, `#category`, both, or just a name (empty Enter or Esc clears) |
|
|
156
152
|
| `e` | edits the selected entry in the quick line, date included |
|
|
@@ -166,29 +162,29 @@ Reopen the terminal and run `kotsu`. To update: `pipx upgrade kotsu-cli` (or run
|
|
|
166
162
|
The line starts with the amount; the rest comes in any order:
|
|
167
163
|
|
|
168
164
|
```text
|
|
169
|
-
89,90
|
|
170
|
-
+3500
|
|
171
|
-
120
|
|
172
|
-
45
|
|
165
|
+
89,90 groceries #food @nubank expense (no sign)
|
|
166
|
+
+3500 salary @itau income
|
|
167
|
+
120 fridge #home @nubank 10x 10 installments of 120,00, one per month
|
|
168
|
+
45 pharmacy yesterday date: today, yesterday, 15/10 or 15/10/2025 (default: today)
|
|
173
169
|
```
|
|
174
170
|
|
|
175
|
-
Every entry needs an `@account`; without one the line is not saved. An account or category that does not exist yet asks whether to create it. Editing or renaming an installment asks "only this one or all?". Amounts use a decimal comma and dates are day/month, as in Brazil.
|
|
171
|
+
Every entry needs an `@account`; without one the line is not saved. An account or category that does not exist yet asks whether to create it. Editing or renaming an installment asks "only this one or all?" (`a` = all, `o` = only this). Amounts use a decimal comma and dates are day/month, as in Brazil.
|
|
176
172
|
|
|
177
173
|
### ↻ Categories that repeat every month
|
|
178
174
|
|
|
179
|
-
When you create a category, Kotsu asks whether it repeats every month (or press `m` on an existing one). Every new entry in it reappears each month with the same amount, account and day (day 31 becomes the last day of the month), with no end, until you stop it. Months appear as you navigate with ← / →, up to 24 months ahead of today. Deleting, editing or renaming one asks "only this month or the
|
|
175
|
+
When you create a category, Kotsu asks whether it repeats every month (or press `m` on an existing one). Every new entry in it reappears each month with the same amount, account and day (day 31 becomes the last day of the month), with no end, until you stop it. Months appear as you navigate with ← / →, up to 24 months ahead of today. Deleting, editing or renaming one asks "only this month or the upcoming ones?" (`o` = only this month, `u` = this and upcoming).
|
|
180
176
|
|
|
181
177
|
## IV. Your data and backups
|
|
182
178
|
|
|
183
179
|
<table>
|
|
184
180
|
<tr>
|
|
185
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
181
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/backup.svg" width="44" alt=""></td>
|
|
186
182
|
<td>
|
|
187
183
|
<ul>
|
|
188
|
-
<li><b>Where it lives:</b> <code>%APPDATA%\
|
|
184
|
+
<li><b>Where it lives:</b> <code>%APPDATA%\kotsu</code> on Windows and <code>~/.local/share/kotsu</code> on Linux (or the folder in the <code>KOTSU_DATA</code> variable). <code>kotsu --data FOLDER</code> opens another folder.</li>
|
|
185
|
+
<li><b>A synced folder (two computers):</b> <code>kotsu --data-folder "G:\My Drive\Kotsu"</code> makes that folder this computer's data folder (it copies the database there the first time). Backups then go to the local folder, outside the sync. While Kotsu is open, a <code>kotsu.lock</code> file tells the other computer it is in use; <code>kotsu --force</code> opens anyway. <code>kotsu --default-data-folder</code> goes back to the local folder.</li>
|
|
189
186
|
<li><b>Manual backup:</b> the <code>b</code> key creates <code>backups/money.YYYY-MM-DD-HHMMSS.sqlite</code> and keeps the 5 most recent copies. Kotsu never backs up on its own.</li>
|
|
190
|
-
<li><b>
|
|
191
|
-
<li><b>Merging with another device:</b> <code>kotsu --exportar data.json</code> writes everything to a file; on the other device, <code>kotsu --importar data.json</code> merges it with what is there (the most recent change to each entry wins, and deleted entries stay deleted). Do it both ways and check: <code>kotsu --conferir</code> shows a short code that is the same on both when the data is the same; <code>kotsu --conferir data.json</code> lists the differences. Before importing, Kotsu saves <code>backups/money.antes-da-troca.sqlite</code>.</li>
|
|
187
|
+
<li><b>Merging with another device:</b> <code>kotsu --export data.json</code> writes everything to a file; on the other device, <code>kotsu --import data.json</code> merges it with what is there (the most recent change to each entry wins, and deleted entries stay deleted). Do it both ways and check: <code>kotsu --verify</code> shows a short code that is the same on both when the data is the same; <code>kotsu --verify data.json</code> lists the differences. Before importing, Kotsu saves <code>backups/money.before-import.sqlite</code>.</li>
|
|
192
188
|
<li><b>No password or encryption:</b> protect your computer and the folder as you would any personal file.</li>
|
|
193
189
|
<li><b>Restore:</b> close the app, move the current <code>money.sqlite</code> elsewhere, copy the chosen backup to <code>money.sqlite</code>, open the app and check the totals.</li>
|
|
194
190
|
</ul>
|
|
@@ -196,11 +192,21 @@ When you create a category, Kotsu asks whether it repeats every month (or press
|
|
|
196
192
|
</tr>
|
|
197
193
|
</table>
|
|
198
194
|
|
|
199
|
-
## V.
|
|
195
|
+
## V. Updating from a Portuguese version
|
|
196
|
+
|
|
197
|
+
Versions before November 2026 had Portuguese names. Updating keeps everything working:
|
|
198
|
+
|
|
199
|
+
- The database is migrated on first open (schema 4: English table and column names). A copy is kept first as `money.before-v4.sqlite` next to the database.
|
|
200
|
+
- `tema.toml` becomes `theme.toml`, the local folder `money-cli` becomes `kotsu`, and the old `MONEY_CLI_DADOS` variable still works.
|
|
201
|
+
- The old flags (`--dados`, `--pasta-dados`, `--forcar`, `--exportar`, `--importar`, `--conferir`) still work, hidden from `--help`.
|
|
202
|
+
- Exchange files from older versions import normally. Update **both** devices before comparing check codes: the code changed with the English names, so an old and a new version never show the same code.
|
|
203
|
+
- The lock file is now `kotsu.lock`; a `kotsu.trava` from a computer not updated yet is still respected.
|
|
204
|
+
|
|
205
|
+
## VI. Under the hood
|
|
200
206
|
|
|
201
207
|
<table>
|
|
202
208
|
<tr>
|
|
203
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
209
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/structure.svg" width="44" alt=""></td>
|
|
204
210
|
<td>Three layers, and a test fails if one of them imports what it must not. Financial rules (totals, installments, the quick-line parser) live in the domain; the screen only displays.</td>
|
|
205
211
|
</tr>
|
|
206
212
|
</table>
|
|
@@ -208,23 +214,23 @@ When you create a category, Kotsu asks whether it repeats every month (or press
|
|
|
208
214
|
```mermaid
|
|
209
215
|
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#41274f", "primaryTextColor": "#ffe9f4", "primaryBorderColor": "#ff7fb8", "lineColor": "#ffa28c", "secondaryColor": "#291b33", "tertiaryColor": "#1d1424", "fontFamily": "Segoe UI, sans-serif"}}}%%
|
|
210
216
|
flowchart LR
|
|
211
|
-
T["
|
|
212
|
-
T --> D["
|
|
217
|
+
T["ui/<br>Textual: display only"] --> A["storage/<br>SQLite, migrations, backup"]
|
|
218
|
+
T --> D["domain/<br>money in cents,<br>installments, quick line"]
|
|
213
219
|
A --> D
|
|
214
220
|
A --> F[("money.sqlite<br>on your computer")]
|
|
215
221
|
```
|
|
216
222
|
|
|
217
|
-
- Money is an integer number of cents, never a `float`; the sign comes from the
|
|
223
|
+
- Money is an integer number of cents, never a `float`; the sign comes from the kind (income or expense).
|
|
218
224
|
- Every write is a transaction: all or nothing. Changed the schema? A numbered migration, with a copy of the database made first.
|
|
219
225
|
- Totals are never stored: SQL computes them per month.
|
|
220
226
|
|
|
221
|
-
Full contributor rules
|
|
227
|
+
Full contributor rules: [AGENTS.md](AGENTS.md).
|
|
222
228
|
|
|
223
|
-
##
|
|
229
|
+
## VII. Development
|
|
224
230
|
|
|
225
231
|
<table>
|
|
226
232
|
<tr>
|
|
227
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
233
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/dev.svg" width="44" alt=""></td>
|
|
228
234
|
<td>Requires Python 3.11+. The tests never touch the network.</td>
|
|
229
235
|
</tr>
|
|
230
236
|
</table>
|
|
@@ -235,18 +241,18 @@ cd kotsu
|
|
|
235
241
|
python -m venv .venv
|
|
236
242
|
# Windows: .venv\Scripts\activate Linux: source .venv/bin/activate
|
|
237
243
|
pip install -e ".[dev]"
|
|
238
|
-
kotsu --
|
|
239
|
-
python
|
|
240
|
-
python
|
|
244
|
+
kotsu --data ./tmp # uses a test folder
|
|
245
|
+
python tools/check.py --quick # lint + fast tests
|
|
246
|
+
python tools/check.py # everything, same as CI
|
|
241
247
|
```
|
|
242
248
|
|
|
243
|
-
##
|
|
249
|
+
## VIII. License
|
|
244
250
|
|
|
245
251
|
<table>
|
|
246
252
|
<tr>
|
|
247
|
-
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
253
|
+
<td width="72" align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/icons/license.svg" width="44" alt=""></td>
|
|
248
254
|
<td><a href="https://github.com/Sera-DAI/kotsu/blob/main/LICENSE">MIT</a>. Made with care, little by little.</td>
|
|
249
255
|
</tr>
|
|
250
256
|
</table>
|
|
251
257
|
|
|
252
|
-
<p align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/
|
|
258
|
+
<p align="center"><img src="https://raw.githubusercontent.com/Sera-DAI/kotsu/main/docs/images/divider.svg" width="420" alt=""></p>
|