ledes-export
Skill1 starsChanged 3 months agoMITView on GitHub
Export a billing invoice to LEDES 1998B format for submission to corporate legal department e-billing systems. Use after generating an invoice exhibit with invoice-generate, when a client's legal ops team requires LEDES format for their matter management or e-billing platform.
In this repository10 other files it gives its agents.
- billing-guardrailsSkill
- billing-reportSkill
- billing-statusSkill
- billing-summarySkill
- cold-start-interviewSkill
- customizeSkill
- invoice-generateSkill
- rate-cardSkill
- time-entrySkill
- wip-reviewSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 3 are distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 25
Read ~/.claude/plugins/config/claude-for-legal/billing/CLAUDE.md.
- Very specific: line 39
If no matching invoice found: "Invoice [id] not found in invoice-register.yaml.
- Very specific: line 41
--client <slug> [--period YYYY-MM]: Read [billing_data_path]/time-register.yaml. - Distinctive: line 43
If neither argument is provided: list clients that have billed or approved entries and ask which one to export.
- Distinctive: line 47
Read the register through the validator, not by hand: Run:
- Distinctive: line 55
Do not recompute them and do not carry figures forward from earlier in the conversation.
- Very specific: line 62
If scripts/register-read.ps1 is not present, this is an install predating it.
- Very specific: line 80
All dates in LEDES 1998B are formatted YYYYMMDD (no separators).
- Very specific: line 86
If loaded from an invoice record, use period_start and period_end instead of computing from entries.
- Very specific: line 92
If original_amount is null, the entry was never written down.
- Very specific: line 93
If original_amount is set, LINE_ITEM_ADJUSTMENT_AMOUNT = original_amount - amount, to 2 decimal places.
- Very specific: line 102
No trailing newline after the last []
- Very specific: line 103
All numeric amounts formatted with exactly 2 decimal places (e.g., 280.00)
- Very specific: line 152
If exporting without a finalized invoice (--client + --period), use the filename [client-slug]-[YYYY-MM].ledes.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/billing-legal:ledes-export
When this runs
An invoice has been generated and the client requires LEDES 1998B format for their e-billing system. This is common for corporate legal departments, insurance companies, and large institutional clients.
Instructions
Preflight - read /billing-legal:billing-guardrails first. It carries the approval gate,
the append-only record rule, UTBMS field discipline, and the boundary on what this plugin
records rather than decides. Where it conflicts with the steps below, the stricter rule wins.
1. Read config
Read ~/.claude/plugins/config/claude-for-legal/billing/CLAUDE.md. Check for placeholders — if present, stop and direct to /billing-legal:cold-start-interview.
Get:
billing_data_pathFirm nameLEDES exportsetting — ifdisabled, warn: "LEDES export is disabled in your billing config. Enable it with/billing-legal:customizeor setLEDES export: enabledin your billing CLAUDE.md. Proceeding anyway — you can always run this skill directly."Default timekeeper classification
2. Resolve scope
Parse $ARGUMENTS:
--invoice <id> (preferred): Load the invoice record from [billing_data_path]/invoice-register.yaml where id: [id]. Get period_start, period_end, client, entry_ids, total_fees, date.
If no matching invoice found: "Invoice [id] not found in invoice-register.yaml. Check /billing-legal:billing-report --invoice [id] or use --client to export by client and period."
--client <slug> [--period YYYY-MM]: Read [billing_data_path]/time-register.yaml. Filter to entries with client: [slug] and status: billed (or approved if --period is used without a finalized invoice). If --period is supplied, restrict to entries in that month.
If neither argument is provided: list clients that have billed or approved entries and ask which one to export.
3. Load entries and attorney profiles
Read the register through the validator, not by hand. Run:
powershell -NoProfile -NonInteractive -File "[billing_data_path]/scripts/register-read.ps1" -Client [client-slug]It resolves the data path from config, confirms the register exists, parses every entry, checks
that each amount equals hours x rate, and emits JSON with per-status totals. Use its numbers.
Do not recompute them and do not carry figures forward from earlier in the conversation.
- Exit
2: the register is absent. Stop, report the path it printed, write nothing. - Exit
3: it parsed but failed an invariant. Show theerrorsarray and stop before any write. A register whose arithmetic does not hold has been edited by something other than this plugin. - Exit
4: config missing or still holds placeholders. Direct to/billing-legal:cold-start-interview.
If scripts/register-read.ps1 is not present, this is an install predating it. Say so, then fall
back to reading the file directly under the rule below.
If time-register.yaml does not exist, STOP. Say: "No time register at [billing_data_path]/time-register.yaml. That file is absent, not empty -- billing data may be unsynced, the data path may be wrong, or the file may have been moved. I am not going to report figures I cannot read." Do not create it, do not proceed with zeroes, and do not answer from earlier in the conversation. Absent is a different condition from empty or comment-only, which is the normal state of a fresh install.
From time-register.yaml, load all entries matching the resolved scope. Exclude entries with status: write-off — zero-dollar lines are not valid in LEDES format.
For each unique attorney slug in the entries, read [billing_data_path]/attorneys/[slug].yaml. Get:
nametimekeeper_id(fall back to attorney slug if field is absent)timekeeper_classification(fall back toDefault timekeeper classificationfrom config, thenAT)
Read [billing_data_path]/clients/[client-slug].yaml. Get:
nameledes_client_id(fall back to client slug uppercased, with hyphens replaced by underscores, e.g.,acme-corp→ACME_CORP)
4. Compute dates
All dates in LEDES 1998B are formatted YYYYMMDD (no separators).
INVOICE_DATE: usedatefrom invoice record, or today's date if exporting outside a finalized invoiceBILLING_START_DATE: earliest entry date in scope, formatted YYYYMMDDBILLING_END_DATE: latest entry date in scope, formatted YYYYMMDD
If loaded from an invoice record, use period_start and period_end instead of computing from entries.
5. Compute write-down adjustment per entry
LEDES requires LINE_ITEM_ADJUSTMENT_AMOUNT — the amount reduced from the original billing through a write-down. The register stores the pre-write-down figures, so this is arithmetic, not inference:
- If
original_amountis null, the entry was never written down.LINE_ITEM_ADJUSTMENT_AMOUNT=0.00. This is the common case. - If
original_amountis set,LINE_ITEM_ADJUSTMENT_AMOUNT=original_amount - amount, to 2 decimal places.amountis already the post-write-down figure, soLINE_ITEM_TOTALstill usesamount. - Never parse
notesto recover a figure.notesholds the attorney's reason in free text, in whatever words they chose. A number reconstructed from a sentence is a guess, and a guess in an adjustment column silently understates what the client was given. Entries written down beforeoriginal_amountexisted carry null and correctly export0.00.
6. Build the LEDES 1998B file
LEDES 1998B format rules:
- Line 1:
LEDES1998B[](literal, no spaces) - Line 2: pipe-delimited column headers, ending with
[] - Lines 3+: one data row per time entry, pipe-delimited, ending with
[] - No trailing newline after the last
[] - All numeric amounts formatted with exactly 2 decimal places (e.g.,
280.00) - Dates formatted YYYYMMDD
- Pipe characters in narrative text must be replaced with a space
Column order (fixed — do not reorder):
INVOICE_DATE|INVOICE_NUMBER|CLIENT_ID|LAW_FIRM_MATTER_ID|INVOICE_TOTAL|BILLING_START_DATE|BILLING_END_DATE|INVOICE_DESCRIPTION|LINE_ITEM_NUMBER|EXP/FEE/INV_ADJ_TYPE|LINE_ITEM_TASK_CODE|LINE_ITEM_EXPENSE_CODE|LINE_ITEM_ACTIVITY_CODE|TIMEKEEPER_ID|TIMEKEEPER_NAME|LINE_ITEM_NUMBER_OF_UNITS|LINE_ITEM_ADJUSTMENT_AMOUNT|LINE_ITEM_TOTAL|LINE_ITEM_DESCRIPTION|LAW_FIRM_MATTER_ID|TIMEKEEPER_CLASSIFICATIONField values per row:
| Column | Value |
| INVOICE_DATE | Invoice date, YYYYMMDD |
| INVOICE_NUMBER | Invoice ID (e.g., INV-2026-007) |
| CLIENT_ID | client.ledes_client_id or slug uppercased |
| LAW_FIRM_MATTER_ID | entry.matter_slug (or empty string if null) |
| INVOICE_TOTAL | Sum of all entry amounts in this export, 2 decimal places — same value on every row |
| BILLING_START_DATE | Period start, YYYYMMDD |
| BILLING_END_DATE | Period end, YYYYMMDD |
| INVOICE_DESCRIPTION | Legal Services - [Firm Name] (ASCII hyphen, never an em dash) |
| LINE_ITEM_NUMBER | Sequential integer starting at 1 |
| EXP/FEE/INV_ADJ_TYPE | F (fee — for all time entries) |
| LINE_ITEM_TASK_CODE | entry.task_code or empty string |
| LINE_ITEM_EXPENSE_CODE | (empty) |
| LINE_ITEM_ACTIVITY_CODE | entry.activity_code or empty string |
| TIMEKEEPER_ID | attorney.timekeeper_id or attorney slug |
| TIMEKEEPER_NAME | attorney.name |
| LINE_ITEM_NUMBER_OF_UNITS | entry.hours (decimal, e.g., 0.80) |
| LINE_ITEM_ADJUSTMENT_AMOUNT | Write-down delta or 0.00 |
| LINE_ITEM_TOTAL | entry.amount, 2 decimal places |
| LINE_ITEM_DESCRIPTION | entry.narrative (pipe chars replaced with spaces) |
| LAW_FIRM_MATTER_ID | entry.matter_slug again (LEDES spec repeats this column) |
| TIMEKEEPER_CLASSIFICATION | attorney.timekeeper_classification |
Example output:
LEDES1998B[]
INVOICE_DATE|INVOICE_NUMBER|CLIENT_ID|LAW_FIRM_MATTER_ID|INVOICE_TOTAL|BILLING_START_DATE|BILLING_END_DATE|INVOICE_DESCRIPTION|LINE_ITEM_NUMBER|EXP/FEE/INV_ADJ_TYPE|LINE_ITEM_TASK_CODE|LINE_ITEM_EXPENSE_CODE|LINE_ITEM_ACTIVITY_CODE|TIMEKEEPER_ID|TIMEKEEPER_NAME|LINE_ITEM_NUMBER_OF_UNITS|LINE_ITEM_ADJUSTMENT_AMOUNT|LINE_ITEM_TOTAL|LINE_ITEM_DESCRIPTION|LAW_FIRM_MATTER_ID|TIMEKEEPER_CLASSIFICATION[]
20260601|INV-2026-007|ACME_CORP|acme-msa-2026|420.00|20260501|20260531|Legal Services - Hartley & Associates LLP|1|F|L200|||aj001|Alice Jones|0.80|0.00|280.00|Reviewed vendor MSA redline; drafted markup on limitation of liability and IP ownership|acme-msa-2026|AT[]
20260601|INV-2026-007|ACME_CORP|acme-msa-2026|420.00|20260501|20260531|Legal Services - Hartley & Associates LLP|2|F|L200|||aj001|Alice Jones|0.40|0.00|140.00|Call with client re counterparty response; revised markup|acme-msa-2026|AT[]7. Write the file
Write to [billing_data_path]/invoices/[invoice-id].ledes.
If exporting without a finalized invoice (--client + --period), use the filename [client-slug]-[YYYY-MM].ledes.
8. Confirm
✓ LEDES 1998B export written:
File: [billing_data_path]/invoices/[filename].ledes
Entries: [N] line items
Total: $[total]
Client: [Client Name] (CLIENT_ID: [ledes_client_id])
Period: [start] – [end]
Write-off entries excluded: [N] (zero-dollar lines are not valid in LEDES format)
To submit: attach this file to your e-billing portal upload.
To view: open in any text editor — it's pipe-delimited plain text.
Tip: if the client's system rejects the file, verify the CLIENT_ID matches
what their e-billing system expects. Update it with:
/billing-legal:rate-card override --client [slug]Notes on LEDES 1998B compatibility
LEDES 1998B is the most widely accepted variant. A few edge cases:
- No task code: Some e-billing systems require LINE_ITEM_TASK_CODE. If entries are missing task codes, the upload may be rejected. Consider enabling
Task codes: requiredin your billing config. - Timekeeper ID: Many corporate clients map timekeeper IDs to their own records. Set
timekeeper_idon attorney profiles to match the ID your client has on file, via/billing-legal:rate-card set --attorney [slug]. - Client ID: Corporate clients often assign their own matter/client IDs. Set
ledes_client_idon the client profile to match, via/billing-legal:rate-card override --client [slug]. - Flat-fee matters: LEDES line items require a unit count and line total. For flat-fee entries, hours are logged but amounts are $0 (or the flat amount divided across entries). The export will include these as-is.
What this skill does not do
- Submit the file to any e-billing system — this is a file export only
- Generate the primary invoice — use
/billing-legal:invoice-generatefirst - Validate against a specific client's LEDES configuration — contact the client's legal ops team for their field requirements
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

