Skip to content

Conversation

@tryonlinux
Copy link
Owner

  • Add complete debt tracking system with SQLite storage
  • Implement snowball, avalanche, and manual sorting modes
  • Add payment tracking with interest/principal breakdown
  • Support optional payment dates (yyyy-mm-dd format)
  • Auto-increment snowball when debts are paid off
  • Support order number or name for all commands
  • Add beautiful terminal UI with Lipgloss tables
  • Hide paid debts from UI while preserving in database
  • Add reset command to clear all data with confirmation
  • Include GitHub Actions for PR testing and releases
  • Add comprehensive test suite (20 test functions)

🤖 Generated with Claude Code

tryonlinux and others added 2 commits December 20, 2025 02:01
- Add complete debt tracking system with SQLite storage
- Implement snowball, avalanche, and manual sorting modes
- Add payment tracking with interest/principal breakdown
- Support optional payment dates (yyyy-mm-dd format)
- Auto-increment snowball when debts are paid off
- Support order number or name for all commands
- Add beautiful terminal UI with Lipgloss tables
- Hide paid debts from UI while preserving in database
- Add reset command to clear all data with confirmation
- Include GitHub Actions for PR testing and releases
- Add comprehensive test suite (20 test functions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
CRITICAL SECURITY FIXES:
- Fix SQL injection vulnerability by replacing string concatenation with explicit query selection
- Restrict database directory permissions from 0755 to 0700 (owner-only)

HIGH PRIORITY FIXES:
- Add database connection cleanup on application exit
- Add input validation to trim whitespace from all user inputs
- Fix duplicate GetSettings call in pay command
- Add database indexes on current_balance and apr columns for improved query performance

CODE QUALITY IMPROVEMENTS:
- Define constants for magic numbers (MaxProjectionMonths, FloatingPointTolerance, MonthsPerYear)
- Update Go version to 1.25 in GitHub Actions workflows
- Add comprehensive TODO.md documenting remaining improvements

All tests passing (20/20).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@github-actions
Copy link

✅ Test Results: PASSED

Test Output
=== RUN   TestCalculateMonthlyInterest
=== RUN   TestCalculateMonthlyInterest/Standard_credit_card_rate
=== RUN   TestCalculateMonthlyInterest/Zero_interest
=== RUN   TestCalculateMonthlyInterest/Low_interest_rate
=== RUN   TestCalculateMonthlyInterest/High_balance,_high_rate
--- PASS: TestCalculateMonthlyInterest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Standard_credit_card_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Zero_interest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Low_interest_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/High_balance,_high_rate (0.00s)
=== RUN   TestProjectPayoffTimeline_SingleDebt
=== RUN   TestProjectPayoffTimeline_SingleDebt/No_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/With_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest)
--- PASS: TestProjectPayoffTimeline_SingleDebt (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/No_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/With_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest) (0.00s)
=== RUN   TestProjectPayoffTimeline_MultipleDebts_Snowball
--- PASS: TestProjectPayoffTimeline_MultipleDebts_Snowball (0.00s)
=== RUN   TestCalculateDebtFreeDate
=== RUN   TestCalculateDebtFreeDate/All_debts_payable
=== RUN   TestCalculateDebtFreeDate/One_unpayable_debt
--- PASS: TestCalculateDebtFreeDate (0.00s)
    --- PASS: TestCalculateDebtFreeDate/All_debts_payable (0.00s)
    --- PASS: TestCalculateDebtFreeDate/One_unpayable_debt (0.00s)
=== RUN   TestProjectPayoffTimeline_ZeroInterest
--- PASS: TestProjectPayoffTimeline_ZeroInterest (0.00s)
=== RUN   TestProjectPayoffTimeline_CascadingSnowball
--- PASS: TestProjectPayoffTimeline_CascadingSnowball (0.00s)
=== RUN   TestAddDebt
=== RUN   TestAddDebt/Add_debt_in_snowball_mode
=== RUN   TestAddDebt/Add_debt_in_manual_mode
=== RUN   TestAddDebt/Duplicate_creditor_name
--- PASS: TestAddDebt (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_snowball_mode (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_manual_mode (0.00s)
    --- PASS: TestAddDebt/Duplicate_creditor_name (0.00s)
=== RUN   TestGetAllDebts_Sorting
=== RUN   TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first)
=== RUN   TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first)
--- PASS: TestGetAllDebts_Sorting (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first) (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first) (0.00s)
=== RUN   TestUpdateDebtBalance
--- PASS: TestUpdateDebtBalance (0.00s)
=== RUN   TestRemoveDebt
--- PASS: TestRemoveDebt (0.00s)
=== RUN   TestRemoveDebt_NotFound
--- PASS: TestRemoveDebt_NotFound (0.00s)
=== RUN   TestUpdateDebtAPR
--- PASS: TestUpdateDebtAPR (0.00s)
=== RUN   TestSetManualOrdering
--- PASS: TestSetManualOrdering (0.00s)
=== RUN   TestClearManualOrdering
--- PASS: TestClearManualOrdering (0.00s)
=== RUN   TestSettings
=== RUN   TestSettings/Get_default_settings
=== RUN   TestSettings/Set_sort_mode
=== RUN   TestSettings/Set_snowball_amount
--- PASS: TestSettings (0.00s)
    --- PASS: TestSettings/Get_default_settings (0.00s)
    --- PASS: TestSettings/Set_sort_mode (0.00s)
    --- PASS: TestSettings/Set_snowball_amount (0.00s)
=== RUN   TestRecordPayment
=== RUN   TestRecordPayment/Record_payment_with_current_date
=== RUN   TestRecordPayment/Record_payment_with_specific_date
--- PASS: TestRecordPayment (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_current_date (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_specific_date (0.00s)
=== RUN   TestGetDebtByCreditor_CaseInsensitive
--- PASS: TestGetDebtByCreditor_CaseInsensitive (0.00s)
=== RUN   TestGetDebtByIndexOrName
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_name
=== RUN   TestGetDebtByIndexOrName/Invalid_index
=== RUN   TestGetDebtByIndexOrName/Nonexistent_name
--- PASS: TestGetDebtByIndexOrName (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_name (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Invalid_index (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Nonexistent_name (0.00s)
=== RUN   TestGetAllDebts_HidesPaidDebts
--- PASS: TestGetAllDebts_HidesPaidDebts (0.00s)
=== RUN   TestResetDatabase
--- PASS: TestResetDatabase (0.00s)
PASS
ok  	github.com/tryonlinux/dave/tests	0.039s

- Add stylish DAVE ASCII art banner to table display
- Include screenshot in README with centered layout
- Add demo data setup scripts (setup-demo.bat/sh) for screenshots
- ASCII art uses cyan color (lipgloss color 86) for visual appeal

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@github-actions
Copy link

✅ Test Results: PASSED

Test Output
=== RUN   TestCalculateMonthlyInterest
=== RUN   TestCalculateMonthlyInterest/Standard_credit_card_rate
=== RUN   TestCalculateMonthlyInterest/Zero_interest
=== RUN   TestCalculateMonthlyInterest/Low_interest_rate
=== RUN   TestCalculateMonthlyInterest/High_balance,_high_rate
--- PASS: TestCalculateMonthlyInterest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Standard_credit_card_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Zero_interest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Low_interest_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/High_balance,_high_rate (0.00s)
=== RUN   TestProjectPayoffTimeline_SingleDebt
=== RUN   TestProjectPayoffTimeline_SingleDebt/No_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/With_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest)
--- PASS: TestProjectPayoffTimeline_SingleDebt (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/No_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/With_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest) (0.00s)
=== RUN   TestProjectPayoffTimeline_MultipleDebts_Snowball
--- PASS: TestProjectPayoffTimeline_MultipleDebts_Snowball (0.00s)
=== RUN   TestCalculateDebtFreeDate
=== RUN   TestCalculateDebtFreeDate/All_debts_payable
=== RUN   TestCalculateDebtFreeDate/One_unpayable_debt
--- PASS: TestCalculateDebtFreeDate (0.00s)
    --- PASS: TestCalculateDebtFreeDate/All_debts_payable (0.00s)
    --- PASS: TestCalculateDebtFreeDate/One_unpayable_debt (0.00s)
=== RUN   TestProjectPayoffTimeline_ZeroInterest
--- PASS: TestProjectPayoffTimeline_ZeroInterest (0.00s)
=== RUN   TestProjectPayoffTimeline_CascadingSnowball
--- PASS: TestProjectPayoffTimeline_CascadingSnowball (0.00s)
=== RUN   TestAddDebt
=== RUN   TestAddDebt/Add_debt_in_snowball_mode
=== RUN   TestAddDebt/Add_debt_in_manual_mode
=== RUN   TestAddDebt/Duplicate_creditor_name
--- PASS: TestAddDebt (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_snowball_mode (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_manual_mode (0.00s)
    --- PASS: TestAddDebt/Duplicate_creditor_name (0.00s)
=== RUN   TestGetAllDebts_Sorting
=== RUN   TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first)
=== RUN   TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first)
--- PASS: TestGetAllDebts_Sorting (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first) (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first) (0.00s)
=== RUN   TestUpdateDebtBalance
--- PASS: TestUpdateDebtBalance (0.00s)
=== RUN   TestRemoveDebt
--- PASS: TestRemoveDebt (0.00s)
=== RUN   TestRemoveDebt_NotFound
--- PASS: TestRemoveDebt_NotFound (0.00s)
=== RUN   TestUpdateDebtAPR
--- PASS: TestUpdateDebtAPR (0.00s)
=== RUN   TestSetManualOrdering
--- PASS: TestSetManualOrdering (0.00s)
=== RUN   TestClearManualOrdering
--- PASS: TestClearManualOrdering (0.00s)
=== RUN   TestSettings
=== RUN   TestSettings/Get_default_settings
=== RUN   TestSettings/Set_sort_mode
=== RUN   TestSettings/Set_snowball_amount
--- PASS: TestSettings (0.00s)
    --- PASS: TestSettings/Get_default_settings (0.00s)
    --- PASS: TestSettings/Set_sort_mode (0.00s)
    --- PASS: TestSettings/Set_snowball_amount (0.00s)
=== RUN   TestRecordPayment
=== RUN   TestRecordPayment/Record_payment_with_current_date
=== RUN   TestRecordPayment/Record_payment_with_specific_date
--- PASS: TestRecordPayment (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_current_date (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_specific_date (0.00s)
=== RUN   TestGetDebtByCreditor_CaseInsensitive
--- PASS: TestGetDebtByCreditor_CaseInsensitive (0.00s)
=== RUN   TestGetDebtByIndexOrName
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_name
=== RUN   TestGetDebtByIndexOrName/Invalid_index
=== RUN   TestGetDebtByIndexOrName/Nonexistent_name
--- PASS: TestGetDebtByIndexOrName (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_name (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Invalid_index (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Nonexistent_name (0.00s)
=== RUN   TestGetAllDebts_HidesPaidDebts
--- PASS: TestGetAllDebts_HidesPaidDebts (0.00s)
=== RUN   TestResetDatabase
--- PASS: TestResetDatabase (0.00s)
PASS
ok  	github.com/tryonlinux/dave/tests	(cached)

@github-actions
Copy link

✅ Test Results: PASSED

Test Output
=== RUN   TestCalculateMonthlyInterest
=== RUN   TestCalculateMonthlyInterest/Standard_credit_card_rate
=== RUN   TestCalculateMonthlyInterest/Zero_interest
=== RUN   TestCalculateMonthlyInterest/Low_interest_rate
=== RUN   TestCalculateMonthlyInterest/High_balance,_high_rate
--- PASS: TestCalculateMonthlyInterest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Standard_credit_card_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Zero_interest (0.00s)
    --- PASS: TestCalculateMonthlyInterest/Low_interest_rate (0.00s)
    --- PASS: TestCalculateMonthlyInterest/High_balance,_high_rate (0.00s)
=== RUN   TestProjectPayoffTimeline_SingleDebt
=== RUN   TestProjectPayoffTimeline_SingleDebt/No_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/With_snowball_amount
=== RUN   TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest)
--- PASS: TestProjectPayoffTimeline_SingleDebt (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/No_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/With_snowball_amount (0.00s)
    --- PASS: TestProjectPayoffTimeline_SingleDebt/Unpayable_debt_(payment_<_interest) (0.00s)
=== RUN   TestProjectPayoffTimeline_MultipleDebts_Snowball
--- PASS: TestProjectPayoffTimeline_MultipleDebts_Snowball (0.00s)
=== RUN   TestCalculateDebtFreeDate
=== RUN   TestCalculateDebtFreeDate/All_debts_payable
=== RUN   TestCalculateDebtFreeDate/One_unpayable_debt
--- PASS: TestCalculateDebtFreeDate (0.00s)
    --- PASS: TestCalculateDebtFreeDate/All_debts_payable (0.00s)
    --- PASS: TestCalculateDebtFreeDate/One_unpayable_debt (0.00s)
=== RUN   TestProjectPayoffTimeline_ZeroInterest
--- PASS: TestProjectPayoffTimeline_ZeroInterest (0.00s)
=== RUN   TestProjectPayoffTimeline_CascadingSnowball
--- PASS: TestProjectPayoffTimeline_CascadingSnowball (0.00s)
=== RUN   TestAddDebt
=== RUN   TestAddDebt/Add_debt_in_snowball_mode
=== RUN   TestAddDebt/Add_debt_in_manual_mode
=== RUN   TestAddDebt/Duplicate_creditor_name
--- PASS: TestAddDebt (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_snowball_mode (0.00s)
    --- PASS: TestAddDebt/Add_debt_in_manual_mode (0.00s)
    --- PASS: TestAddDebt/Duplicate_creditor_name (0.00s)
=== RUN   TestGetAllDebts_Sorting
=== RUN   TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first)
=== RUN   TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first)
--- PASS: TestGetAllDebts_Sorting (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Snowball_mode_sorting_(lowest_balance_first) (0.00s)
    --- PASS: TestGetAllDebts_Sorting/Avalanche_mode_sorting_(highest_rate_first) (0.00s)
=== RUN   TestUpdateDebtBalance
--- PASS: TestUpdateDebtBalance (0.00s)
=== RUN   TestRemoveDebt
--- PASS: TestRemoveDebt (0.00s)
=== RUN   TestRemoveDebt_NotFound
--- PASS: TestRemoveDebt_NotFound (0.00s)
=== RUN   TestUpdateDebtAPR
--- PASS: TestUpdateDebtAPR (0.00s)
=== RUN   TestSetManualOrdering
--- PASS: TestSetManualOrdering (0.00s)
=== RUN   TestClearManualOrdering
--- PASS: TestClearManualOrdering (0.00s)
=== RUN   TestSettings
=== RUN   TestSettings/Get_default_settings
=== RUN   TestSettings/Set_sort_mode
=== RUN   TestSettings/Set_snowball_amount
--- PASS: TestSettings (0.00s)
    --- PASS: TestSettings/Get_default_settings (0.00s)
    --- PASS: TestSettings/Set_sort_mode (0.00s)
    --- PASS: TestSettings/Set_snowball_amount (0.00s)
=== RUN   TestRecordPayment
=== RUN   TestRecordPayment/Record_payment_with_current_date
=== RUN   TestRecordPayment/Record_payment_with_specific_date
--- PASS: TestRecordPayment (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_current_date (0.00s)
    --- PASS: TestRecordPayment/Record_payment_with_specific_date (0.00s)
=== RUN   TestGetDebtByCreditor_CaseInsensitive
--- PASS: TestGetDebtByCreditor_CaseInsensitive (0.00s)
=== RUN   TestGetDebtByIndexOrName
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode
=== RUN   TestGetDebtByIndexOrName/Get_by_name
=== RUN   TestGetDebtByIndexOrName/Invalid_index
=== RUN   TestGetDebtByIndexOrName/Nonexistent_name
--- PASS: TestGetDebtByIndexOrName (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_snowball_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_index_-_avalanche_mode (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Get_by_name (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Invalid_index (0.00s)
    --- PASS: TestGetDebtByIndexOrName/Nonexistent_name (0.00s)
=== RUN   TestGetAllDebts_HidesPaidDebts
--- PASS: TestGetAllDebts_HidesPaidDebts (0.00s)
=== RUN   TestResetDatabase
--- PASS: TestResetDatabase (0.00s)
PASS
ok  	github.com/tryonlinux/dave/tests	(cached)

@tryonlinux
Copy link
Owner Author

Vibe Coded, YOLO, YMMV, NO Warranty, I Just wanted to replace an excel spreadsheet with a cli LOL

@tryonlinux tryonlinux merged commit a04b7ab into main Dec 20, 2025
1 check passed
@tryonlinux tryonlinux deleted the dave-1 branch December 20, 2025 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants