A free, open-source, self-hosted expense-splitting platform. Track shared expenses, split bills, and settle debts with friends and groups.
OpenSplit provides a REST API backend that you host yourself, a web frontend, a TypeScript SDK for building client applications, an MCP server for AI agent integration, and an AI-powered expense creation assistant.
Note: The web UI is not production-ready β it is a quick demo of how to use the backend, MCP server, and AI agent. The primary interface is the REST API and SDK.
| Home Page | Friends Page |
|---|---|
![]() |
![]() |
| AI Agent Chat | Swagger API Docs |
|---|---|
![]() |
![]() |
- Home Page β per-currency balance summary for each friend (left) and the add expense form with "Create with AI" button (right)
- Friends Page β accordion list of friends with multi-currency balances on each header, expandable to see individual shared expenses
- AI Agent Chat β floating chat popover that creates expenses from natural language (e.g., "I paid 50 euros and Sam paid 20 euros for a car trip")
- Swagger API Docs β auto-generated interactive API documentation at
/api
opensplit/
βββ apps/
β βββ web/ # Next.js web frontend
βββ packages/
β βββ api/ # NestJS backend (REST API)
β βββ sdk/ # TypeScript SDK for consuming the API
β βββ mcp/ # MCP server for AI agent integration
β βββ ai/ # AI agent for natural language expense creation
βββ docker-compose.yml
βββ package.json
Monorepo managed with pnpm workspaces.
| Package | Description | Stack |
|---|---|---|
@opensplit/web |
Web frontend | Next.js 15, shadcn/ui, Tailwind CSS |
@opensplit/api |
Self-hosted REST API server | NestJS, Prisma, SQLite (configurable) |
@opensplit/sdk |
Typed client SDK (zero dependencies) | TypeScript, native fetch |
@opensplit/mcp |
MCP server for AI agent integration | MCP SDK, @opensplit/sdk |
@opensplit/ai |
AI agent for natural language expense creation | Vercel AI SDK, OpenAI, @ai-sdk/mcp |
- Users β registration, profiles, API key authentication
- Groups β create groups for home, trips, couples, etc.
- Friends β add friends, track balances between any two users
- Expenses β create, split equally or by custom shares, with full validation
- AI expense creation β create expenses from natural language via a chat interface powered by OpenAI
- Comments β comment on any expense
- Notifications β activity feed for expense/group changes
- Categories β pre-seeded category hierarchy (Food, Transportation, Utilities, etc.)
- Multi-currency β 37 pre-seeded currencies with per-currency balance tracking (each currency shows its own "owes you" / "you owe" direction independently)
- Soft-delete β expenses and groups are soft-deleted and can be restored
- Rate limiting β per-IP throttling (5 req/min on auth, 100 req/min globally)
- Swagger β auto-generated API documentation at
/api
OpenSplit uses Prisma as its ORM, which means you choose your database. The schema ships with SQLite as the default β zero setup, no external services, just works. For production or larger deployments, you can switch to any Prisma-supported provider:
| Provider | provider value |
DATABASE_URL example |
Best for |
|---|---|---|---|
| SQLite (default) | "sqlite" |
file:./opensplit.db |
Getting started, small teams, single-server deployments |
| PostgreSQL | "postgresql" |
postgresql://user:pass@localhost:5432/opensplit |
Production, high concurrency |
| MySQL | "mysql" |
mysql://user:pass@localhost:3306/opensplit |
Production, existing MySQL infrastructure |
| SQL Server | "sqlserver" |
sqlserver://localhost:1433;database=opensplit;... |
Enterprise environments |
- Change the
providerinpackages/api/prisma/schema.prisma:
datasource db {
provider = "postgresql" // change from "sqlite" to your choice
url = env("DATABASE_URL")
}- Update
DATABASE_URLin your.envfile - Re-generate and migrate:
pnpm db:generate
pnpm db:migrate
pnpm db:seedA docker-compose.postgres.yml override is included for PostgreSQL deployments.
- Node.js >= 20
- pnpm >= 9
- An OpenAI API key (only needed for the AI chat feature)
No database installation needed β SQLite is the default and requires no setup.
git clone https://github.com/your-username/opensplit.git
cd opensplit
pnpm install# API server
cp packages/api/.env.example packages/api/.env
# MCP server
cp packages/mcp/.env.example packages/mcp/.env
# Web frontend
cp apps/web/.env.example apps/web/.envEdit the .env files as needed. Here are all the environment variables across packages:
packages/api/.env
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | file:./opensplit.db |
Database connection string |
API_PORT |
No | 3000 |
Port the API server listens on |
packages/mcp/.env
| Variable | Required | Default | Description |
|---|---|---|---|
OPENSPLIT_BASE_URL |
No | http://localhost:3000 |
URL of the OpenSplit API |
OPENSPLIT_MCP_PORT |
No | 3001 |
Port for the MCP HTTP server |
OPENSPLIT_API_KEY |
Stdio only | β | API key for stdio mode |
apps/web/.env
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_BRAND_NAME |
No | OpenSplit |
Brand name displayed in the UI |
OPENSPLIT_API_URL |
No | http://localhost:3000 |
URL of the OpenSplit API |
OPENSPLIT_MCP_URL |
No | http://localhost:3001 |
URL of the MCP server (for AI chat) |
OPENAI_API_KEY |
For AI chat | β | OpenAI API key for the AI expense creation feature |
# Generate the Prisma client
pnpm db:generate
# Create tables (run migrations)
pnpm db:migrate
# Seed currencies, categories, and a demo user
pnpm db:seedThe seed script will print a demo API key β save it for testing.
You need three processes running:
# Terminal 1: API server
pnpm dev
# Terminal 2: MCP server (HTTP mode)
pnpm --filter @opensplit/mcp build
node packages/mcp/dist/index.js --http
# Terminal 3: Web frontend
pnpm --filter @opensplit/web dev- API starts on
http://localhost:3000(or your configuredAPI_PORT). Swagger docs at/api. - MCP server starts on
http://localhost:3001(or your configuredOPENSPLIT_MCP_PORT). - Web app starts on
http://localhost:3100.
Make sure the ports in your .env files are consistent β OPENSPLIT_API_URL and OPENSPLIT_MCP_URL in the web app must match the actual API and MCP server ports.
Run OpenSplit with Docker (SQLite, no database service needed):
- Set the host ports in
docker-compose.ymlβ replace<host-api-port>,<host-web-port>, and<host-mcp-port>with your desired ports (e.g.3000,3100, and3001) - Set
OPENAI_API_KEYfor the AI chat feature (optional) - Start the services:
OPENAI_API_KEY=sk-... docker compose upThis starts:
- OpenSplit API on your chosen API port β SQLite, migrations and seeding run automatically
- Web frontend on your chosen web port β connects to the API and MCP server internally
- MCP server on your chosen MCP port β HTTP mode, connects to the API internally
You can customize the brand name via the BRAND_NAME env var: BRAND_NAME=MyApp docker compose up
To run in the background:
docker compose up -dTo rebuild after code changes:
docker compose up --buildFor production deployments with PostgreSQL:
- Change
providerto"postgresql"inpackages/api/prisma/schema.prisma - Run with the PostgreSQL override:
docker compose -f docker-compose.yml -f docker-compose.postgres.yml upOpenSplit uses API key authentication. Register or log in to get your API key, then include it as a Bearer token in every request:
Authorization: Bearer <your-api-key>
Create a new account. Returns the user profile and an API key.
curl -X POST http://localhost:3000/auth/register \
-H 'Content-Type: application/json' \
-d '{"email": "alice@example.com", "password": "securepassword", "firstName": "Alice"}'Authenticate with an existing account. Returns the user profile and API key.
curl -X POST http://localhost:3000/auth/login \
-H 'Content-Type: application/json' \
-d '{"email": "alice@example.com", "password": "securepassword"}'Generate a new API key (invalidates the old one). Requires authentication.
curl -X POST http://localhost:3000/auth/rotate-key \
-H 'Authorization: Bearer <your-api-key>'When you run pnpm db:seed, a demo user is created (demo@opensplit.dev / demo-password) and its API key is printed to the console.
All endpoints are rate-limited per IP address to prevent abuse:
| Endpoints | Limit | Window |
|---|---|---|
Auth (/auth/*) |
5 requests | 60 seconds |
| All other routes | 100 requests | 60 seconds |
Exceeding the limit returns 429 Too Many Requests. Each IP has its own independent counter β one user hitting the limit does not affect others.
Once the server is running, full interactive API docs are available at:
http://localhost:3000/api
| Method | Path | Description |
|---|---|---|
POST |
/auth/register |
Register a new account |
POST |
/auth/login |
Log in to an existing account |
POST |
/auth/rotate-key |
Generate a new API key (authenticated) |
| Method | Path | Description |
|---|---|---|
GET |
/users/me |
Get current authenticated user |
GET |
/users/:id |
Get user by ID |
PATCH |
/users/:id |
Update user profile |
| Method | Path | Description |
|---|---|---|
GET |
/groups |
List your groups |
GET |
/groups/:id |
Get group with members and balances |
POST |
/groups |
Create a group |
DELETE |
/groups/:id |
Delete a group (soft-delete) |
POST |
/groups/:id/restore |
Restore a deleted group |
POST |
/groups/:id/members |
Add a member to a group |
DELETE |
/groups/:id/members/:userId |
Remove a member from a group |
| Method | Path | Description |
|---|---|---|
GET |
/friends |
List your friends with balances |
GET |
/friends/:id |
Get friend details with balance |
POST |
/friends |
Add a friend (by userId or email) |
DELETE |
/friends/:id |
Remove a friend |
| Method | Path | Description |
|---|---|---|
GET |
/expenses |
List expenses (with filters) |
GET |
/expenses/:id |
Get expense with shares and comments |
POST |
/expenses |
Create an expense |
PATCH |
/expenses/:id |
Update an expense |
DELETE |
/expenses/:id |
Delete an expense (soft-delete) |
POST |
/expenses/:id/restore |
Restore a deleted expense |
Query parameters for GET /expenses:
group_idβ filter by groupfriend_idβ filter by frienddated_after/dated_beforeβ filter by expense dateupdated_after/updated_beforeβ filter by last updatelimit(default: 20, max: 100) /offset
| Method | Path | Description |
|---|---|---|
GET |
/expenses/:expenseId/comments |
List comments on an expense |
POST |
/expenses/:expenseId/comments |
Add a comment |
DELETE |
/comments/:id |
Delete a comment |
| Method | Path | Description |
|---|---|---|
GET |
/notifications |
List your notifications |
Query parameters: updated_after, limit
| Method | Path | Description |
|---|---|---|
GET |
/currencies |
List all supported currencies |
GET |
/categories |
List all categories with subcategories |
Install the SDK in your React, Next.js, or any TypeScript project:
npm install @opensplit/sdkimport { OpenSplitClient } from '@opensplit/sdk';
// With an existing API key
const openSplit = new OpenSplitClient({
baseUrl: 'http://localhost:3000',
apiKey: 'your-api-key-here',
});
// Or without a key β register/login first, then set it
const client = new OpenSplitClient({ baseUrl: 'http://localhost:3000' });
const { user, apiKey } = await client.auth.register({
email: 'alice@example.com',
password: 'securepassword',
firstName: 'Alice',
});
client.setApiKey(apiKey);// Get current user
const me = await openSplit.users.me();
console.log(`Hello, ${me.firstName}!`);
// List your groups
const groups = await openSplit.groups.list();
// Create a group
const group = await openSplit.groups.create({
name: 'Weekend Trip',
groupType: 'TRIP',
members: ['user-id-1', 'user-id-2'],
});
// Create an expense split equally
const expense = await openSplit.expenses.create({
groupId: group.id,
description: 'Dinner',
cost: 120,
currencyCode: 'USD',
splitEqually: true,
});
// Create an expense with custom shares
const customExpense = await openSplit.expenses.create({
description: 'Groceries',
cost: 50,
currencyCode: 'USD',
shares: [
{ userId: 'user-1', paidShare: 50, owedShare: 25 },
{ userId: 'user-2', paidShare: 0, owedShare: 25 },
],
});
// List expenses with filters
const expenses = await openSplit.expenses.list({
group_id: group.id,
dated_after: '2026-01-01',
limit: 50,
});
// Add a comment
const comment = await openSplit.comments.create(expense.id, {
content: 'Including tip',
});
// Get friends with balances
const friends = await openSplit.friends.list();
// Get all currencies
const currencies = await openSplit.currencies.list();
// Get all categories
const categories = await openSplit.categories.list();import { OpenSplitClient, OpenSplitError } from '@opensplit/sdk';
try {
const expense = await openSplit.expenses.get('non-existent-id');
} catch (error) {
if (error instanceof OpenSplitError) {
console.error(`API error ${error.statusCode}: ${error.message}`);
}
}| Resource | Methods |
|---|---|
openSplit.auth |
register(data), login(data), rotateKey() |
openSplit.users |
me(), get(id), update(id, data) |
openSplit.groups |
list(), get(id), create(data), delete(id), restore(id), addMember(groupId, data), removeMember(groupId, userId) |
openSplit.friends |
list(), get(id), create(data), delete(id) |
openSplit.expenses |
list(params?), get(id), create(data), update(id, data), delete(id), restore(id) |
openSplit.comments |
list(expenseId), create(expenseId, data), delete(commentId) |
openSplit.notifications |
list(params?) |
openSplit.currencies |
list() |
openSplit.categories |
list() |
OpenSplit includes an MCP (Model Context Protocol) server that lets AI agents interact with your expenses through natural language. For example:
"Sam and Charlie paid for a birthday gift. Sam paid 19 euros and Charlie paid 37 euros. Split the expense among them."
The AI agent calls create_expense with the right shares β no manual API calls needed.
- OpenSplit API running (locally or hosted)
- An API key (from
POST /auth/registerorPOST /auth/login)
This mode is for individual developers using an MCP-compatible AI client on their machine. The client spawns the MCP server as a child process.
Add this to your MCP client config:
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"opensplit": {
"command": "npx",
"args": ["-y", "@opensplit/mcp"],
"env": {
"OPENSPLIT_API_KEY": "your-api-key",
"OPENSPLIT_BASE_URL": "http://localhost:3000"
}
}
}
}Claude Code (.claude/settings.json):
{
"mcpServers": {
"opensplit": {
"command": "npx",
"args": ["-y", "@opensplit/mcp"],
"env": {
"OPENSPLIT_API_KEY": "your-api-key",
"OPENSPLIT_BASE_URL": "http://localhost:3000"
}
}
}
}This mode runs the MCP server as a standalone HTTP service. Use this when building a product where your AI agent backend connects to the MCP server over HTTP. Each request is authenticated individually β pass the user's API key in the Authorization header.
OPENSPLIT_BASE_URL=http://localhost:3000 npx @opensplit/mcp --httpThe server starts on http://localhost:3001/mcp. Your AI agent sends JSON-RPC requests via POST /mcp with the user's API key:
curl -X POST http://localhost:3001/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Authorization: Bearer <user-api-key>' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'ββββββββββββ ββββββββββββββββ ββββββββββββββββββ ββββββββββββββββ
β User βββββ>β Your AI βββββ>β OpenSplit MCP βββββ>β OpenSplit β
β Browser β β Agent β β :3001/mcp β β API :3000 β
ββββββββββββ ββββββββββββββββ ββββββββββββββββββ ββββββββββββββββ
(passes user's (forwards
API key via API key to
Authorization OpenSplit API)
header)
When using Docker Compose, the MCP server starts automatically in HTTP mode and connects to the API container internally.
| Variable | Required | Default | Description |
|---|---|---|---|
OPENSPLIT_API_KEY |
Stdio only | β | API key for stdio mode (set in MCP client config) |
OPENSPLIT_BASE_URL |
No | http://localhost:3000 |
URL of the OpenSplit API |
OPENSPLIT_MCP_TRANSPORT |
No | stdio |
Set to http for HTTP mode (alternative to --http flag) |
OPENSPLIT_MCP_PORT |
No | 3001 |
Port for HTTP mode |
The MCP server exposes 15 tools that AI agents can call:
| Tool | Description |
|---|---|
| Expenses | |
create_expense |
Create an expense with equal or custom splits |
list_expenses |
List expenses with filters (group, friend, date range) |
get_expense |
Get expense details with shares and comments |
update_expense |
Update an existing expense |
delete_expense |
Soft-delete an expense |
| Groups | |
list_groups |
List your groups |
get_group |
Get group with members and balances |
create_group |
Create a new group |
add_group_member |
Add a user to a group |
| Friends | |
list_friends |
List friends with balances |
get_friend |
Get friend details |
add_friend |
Add a friend by user ID or email |
| Reference | |
list_currencies |
List supported currencies |
list_categories |
List expense categories |
get_current_user |
Get your profile and user ID |
When a user says: "I paid $60 for dinner with Alex. Split it equally."
The AI agent:
- Calls
get_current_userto get your user ID - Calls
list_friendsto find Alex's user ID - Calls
create_expensewith{ description: "Dinner", cost: 60, currencyCode: "USD", splitEqually: true, shares: [...] }
The AI agent package enables natural language expense creation in the web frontend. It connects to the MCP server using Vercel AI SDK and streams responses from OpenAI.
User: "I paid 50 euros and Sam paid 20 euros for a car trip"
β Chat UI (useChat) β /api/chat route
β @opensplit/ai (streamText + OpenAI)
β MCP server (tools: get_current_user, list_friends, create_expense)
β OpenSplit API
- The web app's
/api/chatroute receives the user message @opensplit/aiconnects to the MCP server over HTTP using@ai-sdk/mcp, authenticated with the logged-in user's API key- OpenAI (via Vercel AI SDK) receives the message + system prompt + available MCP tools
- The model calls MCP tools as needed (lookup friends, categories, then create the expense)
- Responses stream back to the chat UI in real-time
The agent is instructed to:
- Resolve friend names to IDs by calling
list_friends - Pick appropriate expense categories automatically
- Default to equal splits, today's date, and the user's default currency
- Confirm with the user before creating any expense
- Handle custom paid amounts (e.g., "I paid 15, Sam paid 20")
| Package | Purpose |
|---|---|
ai |
Vercel AI SDK core β streamText, convertToModelMessages, stepCountIs |
@ai-sdk/mcp |
MCP client β connects to the OpenSplit MCP server over HTTP |
@ai-sdk/openai |
OpenAI provider for Vercel AI SDK |
The web frontend (apps/web) is a Next.js 15 app using shadcn/ui components and Tailwind CSS. It communicates with the API exclusively through server-side SDK calls β the API key is stored in an HTTP-only cookie and never exposed to client-side JavaScript.
Note: The web UI is a demo application to showcase the backend, MCP server, and AI agent capabilities. It is not intended as a production-ready frontend.
| Path | Description |
|---|---|
/login |
Sign in with email and password |
/register |
Create a new account β displays the API key as a "Secret" to save |
/ |
Home β per-currency balance summary for each friend (left), add expense form (right) |
/friends |
Friends list β accordion with per-currency balances on each header, expandable to see individual expenses |
/profile |
Update name, rotate API key |
- Server components by default β data fetching happens on the server via the SDK. Client components are used only for forms and interactive elements.
- Suspense streaming β the friends list and expense form load inside
<Suspense>boundaries with skeleton fallbacks, so the page shell renders instantly. - Auth via HTTP-only cookie β the API key is set as an HTTP-only cookie after login/register. Middleware redirects unauthenticated users to
/login. All API calls go through Next.js server actions or server components. - i18n ready β all UI strings are externalized via next-intl, with English as the default locale. Add new locales by creating translation files in
apps/web/src/messages/. - AI chat β a floating chat popover (bottom-right) powered by
@opensplit/ai. Click the chat bubble or the "Create with AI" button on the expense form to open it. - Invited users β when you add a friend by email who hasn't registered yet, a placeholder account is created with
INVITEDstatus. They can register later with the same email to claim their account and see all shared expenses. - Brand name from env β set
NEXT_PUBLIC_BRAND_NAMEinapps/web/.envto customize the app name shown in the navbar and auth pages.
| Variable | Required | Default | Description |
|---|---|---|---|
NEXT_PUBLIC_BRAND_NAME |
No | OpenSplit |
Brand name displayed in the UI |
OPENSPLIT_API_URL |
No | http://localhost:3000 |
URL of the OpenSplit API (server-side only) |
OPENSPLIT_MCP_URL |
No | http://localhost:3001 |
URL of the MCP server (for AI chat feature) |
OPENAI_API_KEY |
For AI chat | β | OpenAI API key for the AI expense creation feature |
User βββ¬ββ GroupMember ββββ Group
β
βββ Friendship
β
βββ ExpenseShare ββββ Expense ββββ Category
β
βββ Comment
β
βββ Notification
Currency (reference data)
Category (hierarchical, self-referencing)
- Expense shares: Every expense has shares defining who paid and who owes. The sum of
paidSharevalues must equal the expense cost, and so must the sum ofowedSharevalues. - Balances: Computed dynamically from expense shares. The net balance between two users is
sum(paidShare) - sum(owedShare)across all their shared expenses, grouped by currency. - Soft-delete: Groups and expenses use
deletedAttimestamps. Deleted records can be restored. - Friendships: Bidirectional β stored once, queried in both directions.
pnpm dev # Start the API in watch mode
pnpm build # Build all packages
pnpm lint # Lint all packages
pnpm test # Run all tests
pnpm db:generate # Generate Prisma client
pnpm db:migrate # Run database migrations
pnpm db:seed # Seed currencies, categories, and demo user
pnpm db:studio # Open Prisma Studio (database GUI)apps/web/
βββ src/
β βββ app/
β β βββ layout.tsx # Root layout
β β βββ (auth)/ # Auth pages (no navbar)
β β β βββ login/page.tsx
β β β βββ register/page.tsx
β β βββ (app)/ # Authenticated pages (with navbar)
β β β βββ page.tsx # Home (friends + expenses)
β β β βββ profile/page.tsx
β β βββ api/chat/route.ts # AI chat API endpoint
β βββ components/
β β βββ shadcn/ # shadcn/ui components
β β βββ chat-popover.tsx # AI chat floating panel
β β βββ *.tsx # App components (navbar, forms, lists)
β βββ lib/
β β βββ api.ts # SDK client factory
β β βββ auth.ts # Cookie management
β β βββ actions/ # Server actions (auth, expenses, friends, profile)
β βββ middleware.ts # Auth redirect middleware
βββ .env.example
βββ components.json # shadcn config
packages/api/
βββ prisma/
β βββ schema.prisma # Database schema
β βββ seed.ts # Seed data (currencies, categories)
βββ src/
β βββ main.ts # App bootstrap, Swagger setup
β βββ app.module.ts # Root module
β βββ prisma/ # Database connection (global)
β βββ auth/ # Auth endpoints (register, login, rotate key)
β βββ common/ # Guards, decorators, filters
β β βββ guards/ # API key auth guard
β β βββ decorators/ # @CurrentUser(), @Public()
β β βββ filters/ # Global exception filter
β βββ users/ # User endpoints
β βββ groups/ # Group endpoints
β βββ friends/ # Friend endpoints
β βββ expenses/ # Expense endpoints (most complex)
β βββ comments/ # Comment endpoints
β βββ notifications/ # Notification endpoints
β βββ currencies/ # Currency list endpoint
β βββ categories/ # Category list endpoint
βββ Dockerfile
packages/sdk/
βββ src/
β βββ index.ts # Main export
β βββ client.ts # OpenSplitClient base class
β βββ types.ts # All TypeScript interfaces
β βββ resources/ # One file per API resource
βββ tsup.config.ts # Build config (CJS + ESM + types)
packages/mcp/
βββ src/
β βββ index.ts # Entry point (stdio + HTTP transports)
β βββ errors.ts # Error formatting for MCP responses
β βββ tools/ # Tool definitions by domain
β βββ expenses.ts # Expense tools (create, list, get, update, delete)
β βββ groups.ts # Group tools (list, get, create, add member)
β βββ friends.ts # Friend tools (list, get, add)
β βββ reference.ts # Currencies, categories, current user
βββ .env.example
βββ Dockerfile # Multi-stage Docker build
βββ tsup.config.ts # Build config
packages/ai/
βββ src/
β βββ index.ts # Public exports
β βββ prompt.ts # System prompt for the AI agent
β βββ handler.ts # Chat handler factory (streamText + MCP client)
βββ tsup.config.ts # Build config (CJS + ESM + types)
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License β see the LICENSE file for details.



