| name | freelance-invoice-skill |
| description | Freelance time tracking and invoice generation. Use when the user wants to log hours, clock in/out, add items, generate invoices, or manage billing for clients. |
Freelance Invoice Skill
Track time, manage line items, and generate PDF invoices.
Prerequisites
This skill requires bun. If bun is not installed, install it first:
curl -fsSL https://bun.sh/install | bash
Config & Data
All user data lives in ~/.config/freelance-invoice-skill/:
config.json — user identity, clients, payment info, output preferences
timelog.json — active (un-invoiced) time and item entries
archive/ — past invoices and their associated entries
IMPORTANT: Always read ~/.config/freelance-invoice-skill/config.json before any operation. If it doesn't exist, run the setup flow (see below).
First-Time Setup
Before anything else, check that bun is available. If not, install it (see Prerequisites above).
If ~/.config/freelance-invoice-skill/config.json is missing, ask the user:
- Your name (for invoice "From" section)
- Your email
- Payment details — ask which method(s) they use:
- Bank: account number + routing number
- PayPal: email
- Venmo: handle
- Other: freeform
- Clients — for each client, ask:
- Client/company name
- Default hourly rate
- Invoice output directory — where PDFs are saved. Default:
~/invoices/
Then write config.json and create the output directory if it doesn't exist.
config.json schema
{
"user": {
"name": "Jane Doe",
"email": "jane@example.com",
"payment": {
"bank": { "account_number": "123456789", "routing_number": "987654321" },
"paypal": { "email": "jane@example.com" }
}
},
"clients": [
{ "name": "Acme Corp", "hourly_rate": 50 }
],
"output_dir": "~/invoices/",
"next_invoice_number": 1
}
Commands
Log Time
User says things like: "log 6 hours for acme", "logged 2.5 hrs acme - editing videos"
Append to timelog.json:
{
"id": "<uuid>",
"client": "Acme Corp",
"date": "2026-03-07",
"hours": 6,
"description": "editing videos",
"type": "time"
}
- Match client name loosely (case-insensitive, partial match)
- If no description given, ask or default to empty
- Confirm after logging: "Logged 6 hours for Acme Corp (editing videos)"
Clock In / Clock Out
"clock in for acme" — create entry with start as Unix epoch milliseconds (avoids timezone issues):
{
"id": "<uuid>",
"client": "Acme Corp",
"date": "2026-03-07",
"start": 1741358100000,
"hours": null,
"description": "",
"type": "time"
}
Use date +%s000 (bash) or Date.now() (JS) to get epoch ms for start.
"clock out" — find the open entry (hours is null), get current epoch ms, calculate hours: (now - start) / 3600000. Round to nearest 0.25. Set hours, remove start.
Supports fractional hours (round to nearest 0.25).
Add Item
"add item for acme: logo design, $200"
{
"id": "<uuid>",
"client": "Acme Corp",
"date": "2026-03-07",
"description": "Logo design",
"cost": 200,
"quantity": 1,
"type": "item"
}
Show Hours / Summary
"show my hours", "what do I have for acme", "outstanding time"
Read timelog.json, group by client, summarize:
- Total hours per client
- Total dollar amount (hours * rate + item costs)
- List entries
Generate Invoice
"generate invoice for acme", "invoice acme for february"
- Read
config.json for user info, client info, and output dir
- Filter
timelog.json for matching client entries
- If user specifies a date range, filter by date
- Otherwise, use ALL un-invoiced entries for that client
- Build the invoice data JSON matching the generator's expected format:
{
"from": {
"name": "...",
"email": "...",
"account_number": "...",
"routing_number": "..."
},
"to": { "name": "Client Name" },
"hourly_rate": 30,
"invoiceNumber": "INV-001",
"date": "2026-03-07",
"items": [
{ "description": "Editing videos (6 hrs)", "quantity": 6, "unitPrice": 30 },
{ "description": "Logo design", "quantity": 1, "unitPrice": 200 }
],
"notes": ""
}
For time entries: quantity = hours, unitPrice = hourly rate.
For item entries: quantity = quantity, unitPrice = cost.
- Write the JSON to a temp file
- Run the PDF generator:
cd <skill_dir>
bun install
bun run generate-pdf.js --input /tmp/invoice-data.json --output "<output_dir>/INV-001-clientname-2026-03.pdf"
<skill_dir> is the directory containing this SKILL.md file.
- Increment
next_invoice_number in config
- Move the invoiced entries from
timelog.json to archive/INV-001-acme-corp-2026-03.json
- Tell the user where the PDF was saved
Add Client
"add client: Acme Corp, $50/hr"
Append to clients array in config.json.
Edit Config
"change my email", "update acme rate to $55"
Read and update config.json accordingly.
Notes
timelog.json is a flat JSON array. Create it as [] if missing.
- Always use
bun instead of node/npm for running scripts and installing deps.
- Client matching should be fuzzy — "acme", "Acme", "acme corp" all match "Acme Corp".
- When generating invoices, ask the user to confirm the line items before generating.
- The
archive/ directory preserves a record of what was invoiced and when.