spendctl 0.1.0__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.
Files changed (55) hide show
  1. spendctl-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
  2. spendctl-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  3. spendctl-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  4. spendctl-0.1.0/.github/dependabot.yml +10 -0
  5. spendctl-0.1.0/.github/workflows/ci.yml +31 -0
  6. spendctl-0.1.0/.github/workflows/release.yml +42 -0
  7. spendctl-0.1.0/.gitignore +39 -0
  8. spendctl-0.1.0/.pre-commit-config.yaml +7 -0
  9. spendctl-0.1.0/ARCHITECTURE.md +155 -0
  10. spendctl-0.1.0/CHANGELOG.md +38 -0
  11. spendctl-0.1.0/CODE_OF_CONDUCT.md +39 -0
  12. spendctl-0.1.0/CONTRIBUTING.md +137 -0
  13. spendctl-0.1.0/LICENSE +21 -0
  14. spendctl-0.1.0/PKG-INFO +142 -0
  15. spendctl-0.1.0/README.md +107 -0
  16. spendctl-0.1.0/SECURITY.md +32 -0
  17. spendctl-0.1.0/pyproject.toml +65 -0
  18. spendctl-0.1.0/src/spendctl/__init__.py +3 -0
  19. spendctl-0.1.0/src/spendctl/__main__.py +5 -0
  20. spendctl-0.1.0/src/spendctl/cli.py +477 -0
  21. spendctl-0.1.0/src/spendctl/config.py +157 -0
  22. spendctl-0.1.0/src/spendctl/dashboard/__init__.py +0 -0
  23. spendctl-0.1.0/src/spendctl/dashboard/app.py +16 -0
  24. spendctl-0.1.0/src/spendctl/dashboard/helpers.py +111 -0
  25. spendctl-0.1.0/src/spendctl/dashboard/pages/1_dashboard.py +572 -0
  26. spendctl-0.1.0/src/spendctl/dashboard/pages/2_spending.py +192 -0
  27. spendctl-0.1.0/src/spendctl/dashboard/pages/3_debt.py +355 -0
  28. spendctl-0.1.0/src/spendctl/dashboard/pages/4_transactions.py +325 -0
  29. spendctl-0.1.0/src/spendctl/dashboard/pages/5_subscriptions.py +189 -0
  30. spendctl-0.1.0/src/spendctl/dashboard/pages/6_loans.py +227 -0
  31. spendctl-0.1.0/src/spendctl/dashboard/pages/7_report.py +333 -0
  32. spendctl-0.1.0/src/spendctl/db.py +112 -0
  33. spendctl-0.1.0/src/spendctl/init_wizard.py +487 -0
  34. spendctl-0.1.0/src/spendctl/models.py +86 -0
  35. spendctl-0.1.0/src/spendctl/queries/__init__.py +81 -0
  36. spendctl-0.1.0/src/spendctl/queries/_utils.py +24 -0
  37. spendctl-0.1.0/src/spendctl/queries/budget.py +172 -0
  38. spendctl-0.1.0/src/spendctl/queries/check_ins.py +149 -0
  39. spendctl-0.1.0/src/spendctl/queries/dashboard.py +135 -0
  40. spendctl-0.1.0/src/spendctl/queries/reports.py +265 -0
  41. spendctl-0.1.0/src/spendctl/queries/subscriptions.py +94 -0
  42. spendctl-0.1.0/src/spendctl/queries/transactions.py +163 -0
  43. spendctl-0.1.0/src/spendctl/schema.sql +103 -0
  44. spendctl-0.1.0/tests/__init__.py +0 -0
  45. spendctl-0.1.0/tests/conftest.py +112 -0
  46. spendctl-0.1.0/tests/test_budget.py +112 -0
  47. spendctl-0.1.0/tests/test_check_ins.py +175 -0
  48. spendctl-0.1.0/tests/test_cli.py +582 -0
  49. spendctl-0.1.0/tests/test_config.py +268 -0
  50. spendctl-0.1.0/tests/test_dashboard.py +147 -0
  51. spendctl-0.1.0/tests/test_db.py +289 -0
  52. spendctl-0.1.0/tests/test_init_wizard.py +214 -0
  53. spendctl-0.1.0/tests/test_reports.py +176 -0
  54. spendctl-0.1.0/tests/test_subscriptions.py +141 -0
  55. spendctl-0.1.0/tests/test_transactions.py +168 -0
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug or unexpected behavior
4
+ title: '[BUG] '
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Bug Description
10
+ <!-- A clear description of what the bug is -->
11
+
12
+ ## Steps to Reproduce
13
+ 1. Run command: `spendctl ...`
14
+ 2. See error: ...
15
+ 3. ...
16
+
17
+ ## Expected Behavior
18
+ <!-- What you expected to happen -->
19
+
20
+ ## Actual Behavior
21
+ <!-- What actually happened -->
22
+
23
+ ## Environment
24
+ - macOS version:
25
+ - Python version: (`python3 --version`)
26
+ - spendctl version: (`spendctl --version`)
27
+
28
+ ## Error Messages
29
+ ```
30
+ <!-- Paste any error messages or stack traces here -->
31
+ ```
32
+
33
+ ## Additional Context
34
+ <!-- Any other relevant information -->
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new feature or enhancement
4
+ title: '[FEATURE] '
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Feature Description
10
+ <!-- Clear description of the feature you'd like to see -->
11
+
12
+ ## Why is this useful?
13
+ <!-- Explain the use case and who would benefit -->
14
+
15
+ ## Example Usage
16
+ <!-- Show how you envision using this feature -->
17
+ ```bash
18
+ spendctl ...
19
+ ```
20
+
21
+ ## Alternatives Considered
22
+ <!-- Are there workarounds or alternative approaches? -->
23
+
24
+ ## Additional Context
25
+ <!-- Any other relevant information, mockups, or examples -->
@@ -0,0 +1,20 @@
1
+ ## What does this PR do?
2
+
3
+ <!-- Brief description of the change -->
4
+
5
+ ## Why is this needed?
6
+
7
+ <!-- What problem does it solve? Link to related issue if applicable -->
8
+
9
+ ## How was this tested?
10
+
11
+ - [ ] Existing tests pass (`pytest`)
12
+ - [ ] New tests added (if applicable)
13
+ - [ ] Manually tested (`spendctl ...`)
14
+
15
+ ## Checklist
16
+
17
+ - [ ] Code follows existing patterns in the codebase
18
+ - [ ] No unnecessary runtime dependencies added
19
+ - [ ] Documentation updated (if user-facing change)
20
+ - [ ] Lint passes (`ruff check src/ tests/`)
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ - package-ecosystem: "github-actions"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - run: pip install ruff
18
+ - run: ruff check src/ tests/
19
+
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12"]
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+ - run: pip install -e ".[dev]"
31
+ - run: pytest --cov --cov-report=term-missing
@@ -0,0 +1,42 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ release:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Extract version from tag
19
+ id: version
20
+ run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
21
+
22
+ - name: Extract changelog for this version
23
+ id: changelog
24
+ run: |
25
+ version="${GITHUB_REF#refs/tags/v}"
26
+ awk "/^## \\[${version}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md > /tmp/release-notes.md
27
+ echo "Release notes:"
28
+ cat /tmp/release-notes.md
29
+
30
+ - name: Create GitHub Release
31
+ uses: softprops/action-gh-release@v2
32
+ with:
33
+ name: ${{ steps.version.outputs.version }}
34
+ body_path: /tmp/release-notes.md
35
+ draft: false
36
+ prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') }}
37
+
38
+ - name: Build package
39
+ run: pip install build && python -m build
40
+
41
+ - name: Publish to PyPI
42
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,39 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ .venv/
10
+ venv/
11
+
12
+ # Testing & Coverage
13
+ .pytest_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # Linting
18
+ .ruff_cache/
19
+
20
+ # Database (personal financial data)
21
+ *.db
22
+
23
+ # Streamlit
24
+ .streamlit/
25
+
26
+ # macOS
27
+ .DS_Store
28
+ **/.DS_Store
29
+
30
+ # Editors
31
+ .vscode/
32
+ .idea/
33
+ *.swp
34
+ *.swo
35
+ *~
36
+
37
+ # Local development
38
+ CLAUDE.md
39
+ uv.lock
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.9.7
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,155 @@
1
+ # Architecture
2
+
3
+ This document explains the high-level design of spendctl for contributors and maintainers.
4
+
5
+ ## Overview
6
+
7
+ spendctl is a Python CLI that tracks personal finances using SQLite. It provides both a command-line interface and a 7-page Streamlit dashboard for visualization.
8
+
9
+ ```
10
+ User ──► CLI (argparse) ──► Query Modules ──► SQLite Database
11
+ User ──► Dashboard (Streamlit) ──► Query Modules ──► SQLite Database
12
+ ```
13
+
14
+ All data operations flow through the query modules — neither the CLI nor the dashboard issue raw SQL directly (except for a few dashboard-specific aggregations in `helpers.py`).
15
+
16
+ ## Directory Structure
17
+
18
+ ```
19
+ src/spendctl/
20
+ ├── __init__.py # Version string
21
+ ├── __main__.py # python -m spendctl entry point
22
+ ├── cli.py # Argparse CLI with 13 commands, --json on every command
23
+ ├── config.py # Paths, accounts, categories, budgets, accessor functions
24
+ ├── db.py # Connection management, schema init, backup
25
+ ├── init_wizard.py # Interactive setup wizard and config editing
26
+ ├── models.py # Dataclasses (Transaction, CheckIn, Subscription)
27
+ ├── schema.sql # DDL: 9 tables, 1 view, 9 indexes
28
+ ├── queries/
29
+ │ ├── __init__.py # Public API re-exports
30
+ │ ├── _utils.py # Shared date utilities (default_month, month_range)
31
+ │ ├── budget.py # budget_vs_actual, remaining_budget, total_income, cushion
32
+ │ ├── check_ins.py # add_check_in, get_latest, reconcile
33
+ │ ├── dashboard.py # account_balance, all_balances, net_worth, debt_progress
34
+ │ ├── reports.py # monthly_summary, cash_flow, spending_by_category
35
+ │ ├── subscriptions.py # CRUD for recurring subscriptions
36
+ │ └── transactions.py # CRUD, filtering, duplicate detection
37
+ └── dashboard/
38
+ ├── app.py # Streamlit entry point (page config + redirect)
39
+ ├── helpers.py # Shared formatting utilities, month selector widget
40
+ └── pages/
41
+ ├── 1_dashboard.py # Overview: sparklines, net worth gauge, budget summary
42
+ ├── 2_spending.py # Category pie/bar charts, budget alerts
43
+ ├── 3_debt.py # Progress bars, payoff projections, interest savings
44
+ ├── 4_transactions.py # Filterable transaction table
45
+ ├── 5_subscriptions.py # Active/paused/canceled subscription list
46
+ ├── 6_loans.py # Student loan payoff tracking
47
+ └── 7_report.py # Printable monthly budget report
48
+ ```
49
+
50
+ ## Config System
51
+
52
+ Config lives at `~/.config/spendctl/config.json` and is created by `spendctl init`. The file defines:
53
+
54
+ - **accounts** — list of account objects with name, type, institution, APR
55
+ - **categories** — list with name, group, and monthly budget amount
56
+ - **income_sources** — expected monthly income by name and amount
57
+ - **targets** — financial goals (target date, emergency fund target)
58
+ - **min_payments** — minimum payments keyed by account name
59
+ - **loans** — optional loan detail groups for student loan tracking
60
+
61
+ `config.py` uses lazy loading: the first call to any accessor function reads and caches the JSON file. Subsequent calls return from cache. After `config edit`, `reload_config()` clears the cache and re-reads from disk.
62
+
63
+ Account type is the config's single source of truth — accessor functions like `get_debt_accounts()` and `get_student_loan_accounts()` derive their results from account types rather than separate config keys.
64
+
65
+ Data paths are derived from the config directory:
66
+
67
+ ```
68
+ ~/.config/spendctl/
69
+ ├── config.json # User configuration
70
+ └── data/
71
+ ├── spendctl.db # Main database
72
+ └── backups/ # Timestamped database backups
73
+ ```
74
+
75
+ ## Database Schema
76
+
77
+ The schema lives in `schema.sql` and is applied via `init_db()` on first connection to a new database.
78
+
79
+ **Lookup tables** (seeded from config on first init):
80
+
81
+ | Table | Purpose |
82
+ |-------|---------|
83
+ | `accounts` | Account name, type, institution, APR, starting balance |
84
+ | `categories` | Category name, group, monthly budget amount |
85
+ | `transaction_types` | Expense, Income, Transfer, Debt Payment, Interest, Reconciliation |
86
+ | `budget_income` | Expected income sources and amounts |
87
+
88
+ **Core tables:**
89
+
90
+ | Table | Purpose |
91
+ |-------|---------|
92
+ | `transactions` | Every financial transaction with date, amount, type, accounts, category |
93
+ | `check_ins` | Periodic balance snapshot headers (date, label, notes) |
94
+ | `check_in_balances` | Normalized balance values per account per check-in |
95
+ | `subscriptions` | Recurring charges with status tracking (Active/Canceled/Paused) |
96
+ | `settings` | Key-value store for runtime settings (e.g., baseline date) |
97
+
98
+ Check-ins use a normalized design: `check_ins` holds the snapshot header, and `check_in_balances` holds one row per account per check-in. This means new accounts can be added without a schema change.
99
+
100
+ **Views:**
101
+
102
+ | View | Purpose |
103
+ |------|---------|
104
+ | `v_monthly_expenses` | Pre-aggregated spending by month and category |
105
+
106
+ **Indexes:** 9 indexes on transactions (date, category, type, from/to account, date+category) and check-ins (date, check-in ID, account name) for common query patterns.
107
+
108
+ All queries use parameterized SQL (`?` placeholders) to prevent injection.
109
+
110
+ ## Query Layer
111
+
112
+ The 6 query modules in `queries/` provide the data access API:
113
+
114
+ | Module | Key Functions | Purpose |
115
+ |--------|--------------|---------|
116
+ | `transactions` | `add_transaction`, `list_transactions` | Transaction CRUD and filtering |
117
+ | `budget` | `budget_vs_actual`, `remaining_budget`, `total_income` | Budget analysis |
118
+ | `check_ins` | `add_check_in`, `get_latest_check_in` | Balance snapshots |
119
+ | `dashboard` | `account_balance`, `all_balances`, `net_worth`, `debt_progress` | Live balance computation |
120
+ | `reports` | `monthly_summary`, `spending_by_category`, `cash_flow` | Reporting and summaries |
121
+ | `subscriptions` | `list_subscriptions`, `active_monthly_total` | Subscription tracking |
122
+
123
+ All functions accept a `sqlite3.Connection` as the first argument and return plain dicts or lists of dicts. This keeps them easily testable with in-memory databases and JSON-serializable for `--json` output.
124
+
125
+ ## CLI
126
+
127
+ `cli.py` uses argparse with a subcommand pattern. Each command has a handler function (`cmd_*`) that:
128
+
129
+ 1. Calls `_config_guard()` to verify config exists before doing any work
130
+ 2. Validates date/month arguments with regex + `date.fromisoformat()`
131
+ 3. Opens a DB connection via `ensure_db()`
132
+ 4. Calls the appropriate query function(s)
133
+ 5. Serializes output: JSON via `json.dumps()` if `--json` is set, otherwise formatted text
134
+
135
+ The `--json` flag is present on every command. Consumers (AI assistants, shell scripts) can reliably pipe any command to `jq` or a parser without checking first.
136
+
137
+ ## Dashboard
138
+
139
+ The Streamlit dashboard is a multi-page app launched via `spendctl dashboard`. It requires the optional `[dashboard]` extras (`streamlit`, `plotly`, `pandas`).
140
+
141
+ - `app.py` sets page config and immediately redirects to the main dashboard page
142
+ - Each page in `pages/` is an independent Streamlit script; Streamlit loads them alphabetically from the directory
143
+ - `helpers.py` provides shared formatting utilities, a consistent color palette, and the month-selector widget used across pages
144
+ - Database connections use `@st.cache_resource` for a single shared connection per server process
145
+
146
+ All charts use Plotly. The dashboard reads account/category configuration from `config.py` to drive chart labels and budget targets, so adding a new account or category in the config is reflected in the dashboard without code changes.
147
+
148
+ ## Testing
149
+
150
+ - **Framework:** pytest with in-memory SQLite fixtures
151
+ - **Fixture:** `conftest.py` provides a fully seeded in-memory database with sample transactions, check-ins, and subscriptions — no disk I/O in tests
152
+ - **Config isolation:** Tests monkeypatch `CONFIG_PATH` to a temp directory and reset `_config_cache` in `autouse` fixtures to prevent state leakage between tests
153
+ - **Coverage:** All query modules, CLI argument validation, config loading, init wizard, and database layer
154
+ - **Test files:** 10 test modules, 221 tests, all passing
155
+ - **Config:** pytest settings and coverage in `pyproject.toml`, lint via ruff
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-03-01
9
+
10
+ ### Added
11
+
12
+ - Interactive setup wizard (`spendctl init`) for first-time configuration
13
+ - Config management commands (`spendctl config show/edit/reset`)
14
+ - 13 CLI commands with `--json` output on every command
15
+ - Transaction tracking: add, list, filter, search, update, delete
16
+ - Account balance computation (transaction-based with starting balance calibration)
17
+ - Budget vs actual comparison with monthly breakdowns
18
+ - Debt paydown progress tracking with per-account metrics
19
+ - Net worth computation grouped by account type
20
+ - Subscription management (active, canceled, paused)
21
+ - Check-in system for periodic balance snapshots (normalized schema)
22
+ - Monthly summary and cash flow reports
23
+ - Spending breakdowns by category and account
24
+ - CSV export
25
+ - Database backup with timestamped copies
26
+ - 7-page Streamlit dashboard (optional `[dashboard]` extra)
27
+ - Overview with sparklines and net worth gauge
28
+ - Spending analysis with category charts
29
+ - Debt progress with payoff projections
30
+ - Transaction browser
31
+ - Subscription tracker
32
+ - Loan detail page
33
+ - Printable monthly report
34
+ - Full community standards: LICENSE, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY
35
+ - CI pipeline with lint + test matrix (Python 3.10-3.12)
36
+ - 221 tests passing
37
+
38
+ [0.1.0]: https://github.com/Jscoats/spendctl/releases/tag/v0.1.0
@@ -0,0 +1,39 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to a positive environment:
15
+
16
+ - Using welcoming and inclusive language
17
+ - Being respectful of differing viewpoints and experiences
18
+ - Gracefully accepting constructive criticism
19
+ - Focusing on what is best for the community
20
+
21
+ Examples of unacceptable behavior:
22
+
23
+ - Trolling, insulting or derogatory comments, and personal or political attacks
24
+ - Public or private harassment
25
+ - Publishing others' private information without explicit permission
26
+ - Other conduct which could reasonably be considered inappropriate
27
+
28
+ ## Enforcement
29
+
30
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
31
+ reported by opening an issue or contacting the project maintainer. All
32
+ complaints will be reviewed and investigated and will result in a response
33
+ that is deemed necessary and appropriate to the circumstances.
34
+
35
+ ## Attribution
36
+
37
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
38
+ version 2.1, available at
39
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
@@ -0,0 +1,137 @@
1
+ # Contributing to spendctl
2
+
3
+ Thanks for your interest in contributing!
4
+
5
+ This project welcomes contributions from the community. Whether you're fixing a bug, adding a feature, or improving documentation, your help is appreciated.
6
+
7
+ ## Reporting Bugs
8
+
9
+ Found a bug? Please [open an issue](https://github.com/Jscoats/spendctl/issues/new?template=bug_report.md) with:
10
+
11
+ - Clear description of the problem
12
+ - Steps to reproduce
13
+ - Expected vs actual behavior
14
+ - Your environment (macOS version, Python version, spendctl version)
15
+ - Relevant error messages or logs
16
+
17
+ **Note:** Please check existing issues first to avoid duplicates.
18
+
19
+ ## Requesting Features
20
+
21
+ Have an idea? [Open a feature request](https://github.com/Jscoats/spendctl/issues/new?template=feature_request.md) with:
22
+
23
+ - Clear description of the feature
24
+ - Why it would be useful
25
+ - Example usage (if applicable)
26
+
27
+ ## Contributing Code
28
+
29
+ ### Prerequisites
30
+
31
+ - Python 3.10+
32
+ - [`uv`](https://docs.astral.sh/uv/) (recommended) or `pip`
33
+
34
+ ### Before You Start
35
+
36
+ 1. **Check existing issues** - Someone might already be working on it
37
+ 2. **Discuss major changes** - Open an issue first for big features/refactors
38
+ 3. **Keep it focused** - One feature/fix per pull request
39
+
40
+ ### Development Setup
41
+
42
+ ```bash
43
+ # Clone the repo
44
+ git clone https://github.com/Jscoats/spendctl.git
45
+ cd spendctl
46
+
47
+ # Install in editable mode (primary -- uses uv)
48
+ uv tool install -e ".[dev]" # Install with dev extras
49
+ uv tool install -e ".[dev]" --force # Reinstall after changes
50
+
51
+ # Run tests
52
+ pytest
53
+ ```
54
+
55
+ **Fallback:** If you are not using `uv`, you can install with pip instead:
56
+ ```bash
57
+ pip install -e ".[dev]"
58
+ ```
59
+
60
+ ### Code Guidelines
61
+
62
+ **Follow existing patterns:**
63
+ - Match the style of surrounding code
64
+ - Keep functions focused and testable
65
+ - Add docstrings to new functions
66
+
67
+ **No unnecessary runtime dependencies:**
68
+ - Keep the core package dependency-free (stdlib only)
69
+ - Dashboard extras (`streamlit`, `plotly`, `pandas`) belong in the `[dashboard]` optional extra
70
+
71
+ **Testing:**
72
+ - Add tests for new features
73
+ - Ensure existing tests pass: `pytest`
74
+ - Test files live in `tests/`
75
+
76
+ **Documentation:**
77
+ - Update README.md if adding user-facing features
78
+ - Add docstrings to new functions
79
+
80
+ ### Pull Request Process
81
+
82
+ 1. **Fork the repo** and create a feature branch
83
+ ```bash
84
+ git checkout -b feature/my-new-feature
85
+ ```
86
+
87
+ 2. **Make your changes**
88
+ - Write clean, readable code
89
+ - Add tests for new functionality
90
+ - Update documentation
91
+
92
+ 3. **Test thoroughly**
93
+ ```bash
94
+ pytest # All tests must pass
95
+ ruff check src/ tests/ # Lint must pass
96
+ ```
97
+
98
+ 4. **Commit with clear messages**
99
+ ```bash
100
+ git commit -m "Add category budget alerts"
101
+ ```
102
+
103
+ 5. **Push and create a pull request**
104
+ ```bash
105
+ git push origin feature/my-new-feature
106
+ ```
107
+
108
+ 6. **Describe your changes** in the PR:
109
+ - What does it do?
110
+ - Why is it needed?
111
+ - How was it tested?
112
+
113
+ ### Code Review
114
+
115
+ - PRs will be reviewed for code quality, test coverage, and alignment with project goals
116
+ - Be patient - reviews may take a few days
117
+ - Be open to feedback and iteration
118
+
119
+ ## Documentation
120
+
121
+ Documentation improvements are always welcome! You can help by:
122
+
123
+ - Fixing typos or unclear wording
124
+ - Adding examples to the README
125
+ - Improving code comments
126
+
127
+ ## Questions?
128
+
129
+ Not sure about something? Open an issue with the `question` label.
130
+
131
+ ## Thank You!
132
+
133
+ Every contribution, no matter how small, helps make this project better. Thank you for being part of the community!
134
+
135
+ ---
136
+
137
+ **By contributing, you agree that your contributions will be licensed under the project's MIT License.**
spendctl-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jscoats
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.