The module file
A module is one file in app/src/modules/, named <id>.json, plus one data file in app/data/. The sidebar entry, the page and the Settings page all come from the module file, so adding the file adds the module and deleting it removes the module. This page is the full format. You do not write these files yourself; it helps to read them when you check what Claude made.
Keys
A module whose block is not installed is left out of the sidebar rather than breaking the app. The kit's orders run from 10 to 70, with About at 70, so a number such as 25 places a module between People and Goals.
Fields
A field is { "key", "label", "kind", "hint"?, "tab"? }.
Rules:
- The first field is
name. A row without a name is not saved. - Fields on the first tab are the table's columns. Fields on later tabs show only in the row's sheet. With no tabs at all, every field is a column.
- When you save a row on screen, keys that no field names are dropped.
Field kinds
Every value is stored as plain text, except lines, which is a list.
An empty field is left out of the row, not stored as "". Sorting works on every kind except text and lines.
Status values
A new row starts at backlog. Blocked is the darkest pill, so it stands out; Done is the lightest.
Type values (category)
A new row starts as task. The set is fixed; a new type means a code change.
Row ids
Every row has an id, made once from its name when it is first saved: lower case, accents removed, anything else turned into -, at most 48 characters. If the id is taken, a number is added: tallow-co-2. The id never changes, even when you rename the row, because other rows point at it: a task's person holds a People row id.
Icons
Any other name shows a pilcrow mark (¶) instead of failing. To use a new icon, ask Claude to add it to app/src/lib/module-icons.ts; that is a small code change.
Full example: Accounts
Sam's first module. The module file, app/src/modules/accounts.json:
{
"id": "accounts",
"title": "Accounts",
"block": "datatable",
"view": "table",
"icon": "layers",
"file": "accounts.json",
"order": 25,
"purpose": "The customer accounts I look after, and where each one stands.",
"fields": [
{ "key": "name", "label": "Account", "kind": "line", "tab": "Details" },
{ "key": "plan", "label": "Plan", "kind": "line", "tab": "Details" },
{ "key": "renewal", "label": "Renewal date", "kind": "date", "tab": "Details" },
{ "key": "health", "label": "Health", "kind": "line", "hint": "Green, amber or red", "tab": "Details" },
{ "key": "contact", "label": "Main contact", "kind": "person", "tab": "Details" },
{ "key": "note", "label": "Notes", "kind": "text", "hint": "What to know before the next call", "tab": "Note" }
]
}
The data file, app/data/accounts.json:
{
"items": [
{
"id": "tallow-co",
"name": "Tallow & Co",
"plan": "Business",
"renewal": "2026-11-30",
"health": "Amber",
"contact": "maya-okafor",
"note": "Wants the renewal proposal a week before the business review."
},
{
"id": "harbourline-logistics",
"name": "Harbourline Logistics",
"plan": "Business",
"renewal": "2027-03-31",
"health": "Amber",
"contact": "daniel-reyes"
},
{
"id": "fernwood-studio",
"name": "Fernwood Studio",
"plan": "Starter",
"health": "Green"
}
]
}
Result: Accounts appears in the sidebar between People and Goals, with five columns (the Details tab) and the notes behind each row's click. To have Claude make it, see Build your first module.