Fluxby - A local-first financial dashboard for visualizing bank transactions. Monorepo structure with TypeScript, React 19, Vite 7, Tauri 2.0, and SQLite.
Architecture: Local-First, Password-Protected UI, Peer-to-Peer sync capable.
- Install dependencies:
npm install - Start UI dev:
npm run dev(starts web app + landing concurrently) - Start full local dev:
npm run dev:all(starts API + web app + landing concurrently) - Start Tauri dev:
npm run dev:tauri - Build:
npm run build - Build packages only:
npm run build:packages - Build Tauri:
npm run build:tauri - Lint:
npm run lint - Lint with auto-fix:
npm run lint:fix - Format:
npm run format - Type check:
npm run typecheck - Run tests:
npm test - Run tests once:
npm run test:run - Test coverage:
npm run test:coverage - Release (interactive):
npm run release - Release dry-run:
npm run release:dry
- Node:
>=22.0.0(seepackage.jsonengines) - npm:
>=10.0.0
On a fresh clone, you can immediately start development:
npm install
npm run devNo build step required - Vite resolves workspace packages directly from source files in dev mode.
The monorepo has three internal packages (@fluxby/shared, @fluxby/database, @fluxby/core). These are resolved differently depending on the context:
| Context | Resolution | Why |
|---|---|---|
npm run dev |
Source files (.ts) |
Vite aliases point to packages/*/src/ |
npm run test:run |
Source files (.ts) |
Vitest aliases point to packages/*/src/ |
npm run build |
Compiled dist (.js) |
Uses standard Node.js resolution |
npm run typecheck |
Compiled dist (.d.ts) |
TypeScript needs declaration files |
- Development: Never needed -
npm run devworks without building - Testing: Never needed -
npm run test:runworks without building - Type checking: Automatically handled -
npm run typecheckincludesbuild:packages - Production builds: Automatically handled -
npm run buildincludes package builds - CI/Release: Automatically handled - build scripts include dependencies
When you modify files in packages/*:
- Dev mode: Changes are picked up immediately via Vite HMR
- Tests: Changes are picked up immediately when running tests
- TypeScript errors: Run
npm run build:packagesthennpm run typecheck
If you need to manually rebuild packages (e.g., for IDE type resolution):
npm run build:packagesIMPORTANT: For complex multi-step tasks, always create a todo list first using the manage_todo_list tool:
- Break down the task into smaller, actionable items
- Mark each todo as in-progress before starting work
- Mark each todo as completed immediately after finishing
- This ensures visibility into progress and proper planning
IMPORTANT: Always run these checks after making changes:
# 1. Run linter to check for issues
npm run lint
# 2. Run type checker
npm run typecheck
# 3. Run tests
npm run test:runOnly commit when all checks pass. Fix any issues before proceeding.
IMPORTANT: When adding new features, ensure the following are included:
-
Onboarding Instructions: If the feature has a UI component, add
data-onboardingattributes and update the onboarding tour inapps/web/src/components/onboarding/OnboardingContext.tsx -
Database Migration: If the feature requires new tables or columns:
- Create a new migration file in
packages/database/src/migrations/ - Follow the naming convention:
00X_feature_name.ts - Update
packages/database/src/migrations/index.tsto include the new migration - Update
LATEST_MIGRATION_VERSION
- Create a new migration file in
-
Demo Data: If the feature stores data, add demo data seeding:
- Add constants to
packages/shared/src/seed-data.ts(shared between API and web) - Update
apps/web/src/lib/data-service.tsincreateDemoData()function - Update
apps/api/src/routes/profiles.tsin the seed-demo endpoint - Ensure demo data is cleared and re-seeded properly
- Add constants to
-
Translations: Add all user-facing strings to both language files:
apps/web/src/lib/i18n/nl.ts(Dutch - primary, includes types)apps/web/src/lib/i18n/en.ts(English)
-
Help Documentation: Update relevant help articles if the feature affects end users
IMPORTANT: Any LLM contributing to this project must ensure that all ESLint, TypeScript, and test errors are fixed, even if they are outside the immediate scope of the task.
The Fluxby web app is 100% local-first and designed to run on GitHub Pages without any backend:
- Web: SQLite runs in browser via WASM, data stored in OPFS (Origin Private File System)
- Desktop (Tauri): SQLite via WASM, data stored in AppLocalData
- No server required: The app works entirely offline in the browser
The API server (apps/api) is optional and intended only for developers who want to build their own interfaces:
- Uses
better-sqlite3(native Node.js SQLite) - Data stored in
data/folder at project root - Provides REST API with Swagger documentation
- NOT used by the main Fluxby web app - the web app uses OPFS directly
Note: The web app does NOT require the API server. It uses the OPFS database directly via
packages/database. The API is a separate tool for developers building custom integrations.
- User sets a password during onboarding
- Password hash stored via PBKDF2 (100k iterations)
- App locks on idle/close - password required to unlock UI
- All tables have:
id(UUID),updated_at,is_deleted,device_id - Conflict resolution: Last-Write-Wins (LWW)
- Device pairing via PeerJS (WebRTC)
- No central server - devices sync directly
CRITICAL: OPFS (Origin Private File System) has high overhead for individual sync operations. Follow these guidelines to ensure good performance:
When inserting, updating, or deleting multiple rows, always wrap the operations in a transaction:
// ❌ Wrong - each INSERT triggers an OPFS sync
for (const item of items) {
await db.runAsync('INSERT INTO table VALUES (?)', [item]);
}
// ✅ Correct - all INSERTs happen in one transaction, single OPFS sync
await db.transactionAsync(async () => {
for (const item of items) {
await db.runAsync('INSERT INTO table VALUES (?)', [item]);
}
});Why: Each individual db.runAsync() outside a transaction causes:
- A Main Thread → Worker hop
- A disk sync to OPFS
- High latency per operation (~10-50ms)
With transactions, all operations share a single commit/sync, dramatically improving performance.
- Profile creation: Seeding categories, rules, and settings
- CSV imports: Inserting multiple transactions
- Bulk updates: Reordering accounts, applying rules to transactions
- Migrations: Any operation touching multiple rows
- Prefer selecting specific columns over
SELECT *for large tables - Use indexes for frequently filtered/sorted columns
- Consider pagination for large result sets
- Cache frequently accessed static data (categories, rules)
- The database runs in a Web Worker to avoid blocking the UI
- All database operations are async and go through the worker
- Minimize worker communication by batching operations
- Use
queryClient.invalidateQueries()efficiently after mutations
- TypeScript strict mode
- Single quotes, 2 space indent
- React functional components with hooks
- TanStack Query for data fetching
- Tailwind CSS
- Bilingual UI (Dutch and English)
This repository uses the Nexus multi-agent orchestration system for complex planning, execution, and review workflows.
The @Nexus agent is the orchestrator that:
- Triages incoming requests to determine which agent(s) are needed
- Delegates work to specialized agents using
runSubagent - Synthesizes multi-agent responses into unified answers
- Maintains context across agent interactions
- Never implements directly - only coordinates
| Agent | File | Purpose |
|---|---|---|
| Nexus | nexus.agent.md |
Orchestrator - delegates to all agents |
| Architect | architect.agent.md |
System design, schemas, local-first architecture |
| Software Developer | software-developer.agent.md |
Implementation, TDD, production code |
| Tech Lead | tech-lead.agent.md |
Code quality, patterns, architectural decisions |
| QA Engineer | qa-engineer.agent.md |
Testing, edge cases, accessibility |
| Security Agent | security.agent.md |
Security audits, OWASP, vulnerabilities |
| Product Manager | product-manager.agent.md |
Requirements, priorities, acceptance criteria |
| UX Designer | ux-designer.agent.md |
User flows, wireframes, interactions |
| Visual Designer | visual-designer.agent.md |
UI polish, animations, styling |
| DevOps | devops.agent.md |
CI/CD, infrastructure, deployment |
| Gamer | gamer.agent.md |
Gamification mechanics, engagement |
| SEO Specialist | seo-specialist.agent.md |
SEO optimization, search rankings |
| Workflow | Prompt | Purpose |
|---|---|---|
| Planning | #nexus-planning |
Orchestrates all agents to create comprehensive feature plans |
| Execution | #nexus-execution |
Takes plans and coordinates implementation |
| Review | #nexus-review |
Comprehensive code review with automatic fixes |
| Sync | #nexus-sync |
Reconciles documentation with actual work done |
| Summary | #nexus-summary |
Project status snapshot (have vs need) |
| Hotfix | #nexus-hotfix |
Expedited workflow for small, well-understood bugs |
All features are tracked in .nexus/toc.md with status lifecycle:
draft → in-progress → review → complete
Feature files are stored in .nexus/features/<feature-slug>/:
plan.md- What we're building and whyexecution.md- Implementation trackingreview.md- Code review findingssummary.md- Status snapshots
Available MCP integrations (.vscode/mcp.json):
- filesystem - Enhanced file operations
- playwright - E2E test automation
- sequential-thinking - Complex problem decomposition
These rules are ABSOLUTE and must NEVER be violated by any agent:
- NEVER Run Interactive Commands - Use non-interactive flags (
-y,--yes) - NEVER Delete Infrastructure Directories -
.nexus/,.github/,.vscode/ - Use
.nexus/tmp/- For all temporary files instead of system/tmp - Clean Up After Yourself - Agents MUST remove temporary files they create
- Ensure all sensitive data is encrypted at rest and in transit.
- Regularly update dependencies to patch known vulnerabilities.
- Use environment variables for storing secrets and API keys.
- Implement role-based access control (RBAC) for API endpoints.
- Run
npm run lintto check TypeScript and ESLint - Run
npm run test:runto run all unit tests - Run
npm run test:coveragefor coverage report - Test API endpoints via Swagger UI or frontend
- Check for TypeScript errors:
npm run typecheck
- Integration tests should be added for new API endpoints.
- Use
vitestfor unit tests andsupertestfor API testing. - Example:
import request from 'supertest';
import app from '../src/app';
describe('GET /api/example', () => {
it('should return 200 and expected data', async () => {
const res = await request(app).get('/api/example');
expect(res.status).toBe(200);
expect(res.body).toHaveProperty('data');
});
});Tests are located in the /tests directory:
tests/shared/- Shared package utility teststests/api/- Backend API teststests/web/- Frontend component/hook tests
See tests/README.md for the test coverage overview table.
IMPORTANT: Every new feature MUST have accompanying tests.
-
API Endpoints: Add tests in
tests/api/for:- Request validation (required params, types)
- Success responses with expected data structure
- Error handling (404, 400, 500)
- Edge cases (empty data, invalid IDs)
-
Utility Functions: Add tests in
tests/shared/for:- All pure functions in
packages/shared/src/ - Date formatting, currency formatting, string utils
- Edge cases (null, undefined, empty strings)
- All pure functions in
-
Frontend Logic: Add tests in
tests/web/for:- Custom hooks
- Complex state logic
- Data transformation functions
- Use
.test.tssuffix for test files - Match the source file structure:
packages/shared/src/utils.ts→tests/shared/utils.test.tsapps/api/src/routes/categories.ts→tests/api/categories.test.tsapps/web/src/hooks/useFilter.ts→tests/web/hooks/useFilter.test.ts
ALWAYS run the full test suite before confirming any changes:
# Run all checks
npm run lint && npm run typecheck && npm run test:runIf any tests fail, fix them before proceeding.
- Landing page runs at
http://localhost:5177 - Web app (OPFS mode) is proxied via landing page at
/app/ - Web app dev server runs on port 5178 (proxied through landing page)
- For web app dev only:
npm run dev:web(runs on port 5178) - For API dev only:
npm run dev:api(runs on port 3001) - API (for developers building custom interfaces):
http://localhost:3001/api - Swagger API Docs:
http://localhost:3001/api/docs - Database is created automatically in OPFS on first load
- CSV imports: drag file to Import page
When starting the development servers you may sometimes hit EADDRINUSE errors because previous dev processes left ports open. Add these steps to reliably free known dev ports before starting.
-
macOS (zsh):
# Kill gracefully (SIGTERM) any processes using our known dev ports lsof -ti:3001,5177,5178 | xargs kill -15 2>/dev/null || true && sleep 0.5
-
Cross-platform (npm script):
npx kill-port 3001 5177 5178 || true
This makes the behavior explicit for new contributors and LLMs that inspect the repository.
API documentation is available via Swagger UI at /api/docs.
IMPORTANT: When adding new endpoints, always add Swagger JSDoc comments:
/**
* @swagger
* /api/example:
* get:
* summary: Short description
* tags: [TagName]
* parameters:
* - in: query
* name: paramName
* schema:
* type: string
* responses:
* 200:
* description: Success response
*/
router.get('/example', (req, res) => { ... });A Bruno collection is available in the apps/api/bruno/ folder for testing API endpoints.
IMPORTANT: When adding new API endpoints, always create a Bruno request file:
- Create a
.brufile in the appropriate folder (apps/api/bruno/accounts/,apps/api/bruno/transactions/, etc.) - Follow the naming convention:
Verb Noun.bru(e.g.,Get All Accounts.bru) - Add documentation in the
docssection - Test the endpoint with Bruno before committing
See apps/api/bruno/README.md for more details and a template.
Developer documentation is available on the landing page (apps/landing) at /docs/*.
IMPORTANT: When adding or modifying API functionality, also update the developer docs:
- New API Endpoints: Add documentation in
apps/landing/src/pages/docs/ - New API Resources: Create a new
Docs*.tsxpage and add to:apps/landing/src/App.tsx(route)apps/landing/src/components/docs/DocsSidebar.tsx(navigation)apps/landing/src/lib/i18n/nl.tsanden.ts(translations)
- Changed Response Formats: Update the code examples and response types
- New Query Parameters: Document in the parameter tables
The Help Center is available at /help and contains documentation for end users.
IMPORTANT: When adding or modifying important features, also update the Help Center docs:
- New UI Features: Add help articles for end users
- Changed Workflows: Update existing help articles
- New Screenshots: Replace
<ScreenshotPlaceholder>components with actual screenshots
/help- Help Center home (split design: User Guide vs Developer Hub)/help/*- User guide articles
- Developer docs updated (
/docs/*) - for API/technical changes - Help Center updated (
/help/*) - for end-user relevant changes - NL and EN translations added for new help content
- Screenshots/placeholders added where needed
- Swagger JSDoc comments added/updated
- Bruno request file added in
apps/api/bruno/folder - Developer docs page updated (if applicable)
- Help Center updated if API change affects end users
- Code examples tested and working
- NL and EN translations added
- Sidebar navigation updated (if new page)
- Route added in App.tsx (if new page)
Key tables: accounts, transactions, categories, budgets, category_rules, imports
IMPORTANT: All user-facing strings MUST be in the language files, never hardcoded.
- Language files are located at
apps/web/src/lib/i18n/andapps/landing/src/lib/i18n/ - Dutch translations:
nl.ts(primary) - English translations:
en.ts - Type definitions are in
nl.ts- update theTranslationKeysinterface when adding new strings - Use the
useLanguage()hook to access translations:const { t } = useLanguage() - String interpolation: Use
{placeholder}syntax, e.g.,'{count} items'and replace with.replace('{count}', value) - Never use template literals with hardcoded text in JSX
Example:
// ❌ Wrong - hardcoded string
alert('Contact added');
// ✅ Correct - using translation
alert(t.addressBook?.contactAdded || 'Contact added');
// ✅ Correct - with interpolation
alert(
(t.addressBook?.namesUpdated || '{count} names updated').replace(
'{count}',
String(count)
)
);- Edit action: Pencil icon, on hover: purple background (
bg-purple-600) with white icon - Delete action: Trash icon, on hover: red background (
bg-red-600) with white icon. Always show confirmation dialog before performing delete - Save/Cancel in edit mode: Check icon (save) and X icon (cancel), on hover: purple background with white icon
- All icon-only buttons MUST have a tooltip explaining the action
- Use
TooltipProvider,Tooltip,TooltipTrigger, andTooltipContent - Tooltip text should use translations (e.g.,
{t.common.edit},{t.common.delete}) - Always use
rounded-mdclass on icon-only buttons (never round/circular)
- Never include icons in card titles
- Never capitalize all words - only capitalize the first word (e.g., "Name cleanup rules" not "Name Cleanup Rules")
IMPORTANT: Do not create local state for notifications. Always use the Global Toast Context.
Import and use the toast hook:
import { useToast } from '@/contexts/ToastContext';
// In your component:
const toast = useToast();
// Show toasts with appropriate variants:
toast.success('Operation completed'); // Green - auto-dismisses
toast.info('Information message'); // Purple - auto-dismisses
toast.warning('Warning message'); // Orange - requires manual dismiss
toast.error('Error occurred'); // Red - requires manual dismiss
toast.error(error); // Also accepts Error objectsAlways show a toast notification for:
- Creating items (contacts, rules, budgets, etc.)
- Deleting items
- Applying batch operations
- Updating items
- Any action that changes data state
- Run
npm run lintbefore committing - Test changes in browser before PR
- Keep UI text translated in both Dutch and English
- Update Swagger docs when adding/modifying API endpoints
- Add Bruno request files for new API endpoints
IMPORTANT: At the end of each task, agents should use the git tools to stage and commit changes themselves, then output the commit message they used.
Format: Use conventional commit format with git commit -m "<type>(<scope>): <description>" -m "<body>"
Types: feat, fix, docs, style, refactor, perf, test, chore, ci, build
Example output at the end of changes:
git commit -m "feat(web): add transaction filtering by date range" -m "- Add DateRangePicker component
- Update useTransactionState hook with date filter
- Add translations for filter labels (NL/EN)
- Update Swagger docs for date query params"The commit message should:
- Use conventional commit format
- Be comprehensive (include all changes in the body)
- List specific files/components changed when helpful
- Mention translations, tests, and docs updates if applicable
- End the commit message body with "---" followed by "Developed by [model name]" on a new line (replacing [model name] with the actual model name used)