Smithable websitedocsGitHub
Menu

Operations

Every change to an app goes through an operation: it checks the change, edits the spec files, regenerates the code, migrates the database and commits a checkpoint, so smithable undo takes it back. This page is generated from the operations themselves (100 of them), so it matches the Smithable it was built with. Run one with smithable op <name> …; smithable op <name> --help shows the same, and smithable mcp offers them as tools to AI agents. Change an app without writing code walks through five of them.

Models and fields

setNamedBy

Say what names a record of a model, its title wherever one record is shown or referred to: one part or several (a field, a reference for that record's own name, or a field one reference away like classType.name), joined with " · "; --none leaves it to the rule again (the first short text, else the first long text).

smithable op setNamedBy <model> [parts] [--field …] [--none …]

Never loses data.

setThread

Say which fields a conversation reads from a model's records: the message's body (a text field), its author (a person or email field) and the side that is ours (a two-valued choice and its value); --none leaves it to the rule again.

smithable op setThread <model> [--body …] [--author …] [--side …] [--ours …] [--none …]

Never loses data.

Example: smithable op setThread Message --body content --author sender --side sender --ours agent

setUnique

Make two or more fields of a model unique together: no two records hold the same values in all of them, among one owner's records (owner), in the whole app (app), or as the access says; --remove takes the rule away. One field alone is unique through setFieldRules.

smithable op setUnique <model> <fields> [--scope …] [--remove …]

Never loses data.

addModel

Add a model with its fields.

smithable op addModel <model> <fields>

Never loses data.

addField

Add a field to a model.

smithable op addField <model> <field> <type> [--formula …] [--min …] [--max …] [--default …] [--list …]

Never loses data.

Example: smithable op addField Incident severity "minor | major | critical"

changeField

Change a field's type, required flag, uniqueness or default.

smithable op changeField <model> <field> <type> [--renames …]

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

Example: smithable op changeField Order status "draft | placed | onItsWay | canceled" --renames shipped=onItsWay

setFieldRules

Set who fills a field and when it may change: by app (the form never shows it), locked (after it is made, or while a condition holds), required while a condition holds; and whether it is unique, and where (owner or app).

smithable op setFieldRules <model> <field> [--by …] [--locked …] [--once …] [--required …] [--unique …]

Never loses data.

Example: smithable op setFieldRules Order total --locked status=shipped

renameField

Rename a field, keeping its data.

smithable op renameField <model> <from> <to>

Never loses data.

removeField

Remove a field and its data.

smithable op removeField <model> <field>

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

renameModel

Rename a kind of record, keeping its records. Every field, page, action, rule and grant that names it follows.

smithable op renameModel <from> <to>

Never loses data.

setCapacity

Limit how many records of a model each referenced record may have, e.g. a session's bookings up to its capacity ("none" as per removes the limit). The capacity is set whole: a where or a deadline left out is lifted.

smithable op setCapacity <model> <per> [max] [where] [--closes …] [--cancelUntil …]

Never loses data.

setSchedule

Say a model's records are booked by their time (barbers, rooms): weekly hours, breaks, minutes between bookings, and a model of time away ("none" as hours removes it).

smithable op setSchedule <model> <hours> [--breaks …] [--gap …] [--away …]

Never loses data.

setOccupies

Say each record of a model takes a scheduled record's time, e.g. an appointment its barber's, from its start for its service's minutes ("none" as resource removes it).

smithable op setOccupies <model> <resource> [starts] [duration] [while] [--cancelUntil …] [--cancelTo …]

Never loses data.

setTrash

Keep a model's deleted records in a trash for thirty days, where whoever may delete them can restore them (true), or delete for good at once (false, the default).

smithable op setTrash <model> <trash>

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

setReview

Hold a model's pictures and files for review before others see them: the fields named, "all" (every picture and file field), or "none". A required one holds the whole record until approved, an optional one only the file; admins and the roles granted review approve them on a review page.

smithable op setReview <model> <fields>

Never loses data.

Pages and navigation

renamePage

Rename a page. Its menus, grants, texts and look follow; without a route of its own, its address becomes / plus the new name.

smithable op renamePage <from> <to>

Never loses data.

removePage

Remove a page, from the menus too. Its records and their data stay.

smithable op removePage <page>

Never loses data.

addPage

Add a page that lists, shows, creates, edits and deletes a model, and add it to the main menu.

smithable op addPage <page> <model> [route]

Never loses data.

setSearch

Let a page's list be searched by these fields, or remove the search (no fields).

smithable op setSearch <page> [fields]

Never loses data.

setFilters

Let a page's list be filtered by these fields (choices, references, numbers, dates, yes/no), or remove the filters (no fields).

smithable op setFilters <page> [fields]

Never loses data.

setDefaultFilter

What a page's list is filtered by until the viewer chooses: a filter and its value, as the address carries it (startsAt upcoming; status "open,pending"); "none" takes the default away.

smithable op setDefaultFilter <page> <field> <value>

Never loses data.

Example: smithable op setDefaultFilter sessions startsAt upcoming

setSortable

Let a page's list be sorted by these fields from a Sort by control, or remove the choice (no fields).

smithable op setSortable <page> [fields]

Never loses data.

setFavorites

Let people save a page's records (a heart on each, and a Saved view), list only the viewer's saved ones (only), or remove favorites (off).

smithable op setFavorites <page> [mode]

Never loses data.

setContact

Give a page's record page a contact block that mails the record's owner (or the user a field names), optionally keeping a copy in a model; no recipient removes it.

smithable op setContact <page> [to] [--keep …]

Never loses data.

setMine

Make a page list only the viewer's own records (my items, my orders), or every record again (off).

smithable op setMine <page> [mode]

Never loses data.

addLandingPage

Add a landing page for visitors, starting with a hero and a footer (add more with addSection). It opens without sign-in unless public is false.

smithable op addLandingPage <page> [route] [--public …]

Never loses data.

Example: smithable op addLandingPage home

addSection

Add a section to a page, below its others: one named after its kind (hero, features, pricing, testimonials, faq, callToAction, contact, gallery, footer), or any name with records (a model's records as cards, a table or a calendar), text (a heading and paragraphs) or custom. Its texts go in smithable.content.yml.

smithable op addSection <page> <section> [--records …] [--view …] [--starts …] [--text …] [--kind …] [--where …] [--form …] [--button …] [--custom …]

Never loses data.

removeSection

Remove a section from a page, with its texts. A page kind's own part (a crud page's list) stays.

smithable op removeSection <page> <section>

Never loses data.

moveSection

Move a page's section above or below another of its sections: they show in the order written. A page kind's own part moves once its name is among the sections.

smithable op moveSection <page> <section> [--before …] [--after …]

Never loses data.

setSectionWhere

Show only some of a records section's records: those with these values (none shows every record).

smithable op setSectionWhere <page> <section> [where]

Never loses data.

Example: smithable op setSectionWhere home open "status=investigating | identified | monitoring" "severity=major | critical"

setShortcuts

Give a landing page's hero up to four shortcuts to other pages, shown as round icons (no pages removes them).

smithable op setShortcuts <page> [pages]

Never loses data.

addSectionsPage

Add a page of parts and no kind (an about page), starting with a text section; add more with addSection. It is in the main menu.

smithable op addSectionsPage <page> [route]

Never loses data.

addTile

Add a tile to a dashboard page (created, and added to the main menu, when it does not exist): a count, sum or average of records, a list of the latest ones, or a custom query's records.

smithable op addTile <page> <tile> <measure> <of> [--where …] [--by …] [--limit …]

Never loses data.

addUsersPage

Add a page where administrators invite people by email address with a role, change roles and remove users; it goes in the main menu. An app has one.

smithable op addUsersPage [page] [route]

Never loses data.

addDataPage

Add an administrators' page with every model's records (or the models named) to find, open, change and delete; it goes in the main menu.

smithable op addDataPage [page] [route] [models]

Never loses data.

addReviewPage

Add the page where the pictures and files that wait for review (a model's review) are approved or rejected, for admins and the roles granted review; it goes in the main menu.

smithable op addReviewPage [page] [route] [models]

Never loses data.

addCalendarPage

Add a page showing a model's records in a week or month calendar, placed by a date or date-time field; it goes in the main menu.

smithable op addCalendarPage <page> <model> [starts]

Never loses data.

addCalculatorPage

Add a calculator page: fields written as a model's, the entered ones its inputs and the formula fields its results, stored nowhere; it goes in the main menu.

smithable op addCalculatorPage <page> <fields>

Never loses data.

Example: smithable op addCalculatorPage mortgage price=money years=int

addArticlesPage

Add a page showing a model's records as articles (a blog or news): a list, and a page per article; it goes in the main menu.

smithable op addArticlesPage <page> <model>

Never loses data.

addCustomPage

Add a page of your own (a chart, a map, a planner): its component is created once in src/custom/pages/ and is yours; the route, access and menu entry stay Smithable's.

smithable op addCustomPage <page> <component>

Never loses data.

Example: smithable op addCustomPage planner Planner

addPrivacyPage

Add a privacy page written from the app itself (who runs it, what it stores and why, cookies, mail, logs, rights); open to visitors and linked from the site's footer. The operator's name and contact go in smithable.content.yml.

smithable op addPrivacyPage [page] [route]

Never loses data.

addOrderPage

Add a page where visitors choose records of one model (dishes) and send them as one order of another (with its lines and a number counting up); it is not in the menu.

smithable op addOrderPage <page> <from> <into> [--ask …] [--route …]

Never loses data.

setNavigation

Set which pages a menu shows, in order. The list replaces the menu: include every page that stays.

smithable op setNavigation <menu> <pages>

Never loses data.

Access and sign-in

grant

Set what a role may do beyond the record rule, in smithable.auth.yml: the pages it opens, the models it adds to (create), reads, changes (write) or deletes for everyone, the custom actions it runs, or the models whose waiting pictures and files it approves (review). "all" names everything, "none" nothing; "grant all" is the whole app and "grant none" the record rule only.

smithable op grant <role> <what> [names]

Never loses data.

Example: smithable op grant customer actions cancel

setSignUp

Choose who may sign in: anyone with an account at the sign-in provider (within allowedDomains, when set), or only invited people and the admins (needs a users page).

smithable op setSignUp <who>

Never loses data.

Example: smithable op setSignUp invited

setDefaultAccess

Choose the record rule: shared (everyone who signs in sees every record) or private (each person sees only their own); everyone changes and deletes their own, and the roles' grants say the rest.

smithable op setDefaultAccess <access>

Never loses data.

Example: smithable op setDefaultAccess shared

setRoles

Set the app's roles with a description each ("admin" always exists and does everything), and which one new users get. A role keeps what it was granted; a new one starts with the record rule only, so grant it its pages.

smithable op setRoles <roles> [--defaultRole …]

Never loses data.

setPublic

Set what anyone on the internet may use without signing in: pages to open, models to read, models to create (a contact form). Each list replaces the old one; an empty list closes it, and the confirmation says what visitors lose.

smithable op setPublic [--pages …] [--read …] [--create …]

Never loses data.

setPublicWhere

Limit which records of a model anyone on the internet may read, e.g. published posts only; with no condition, every record.

smithable op setPublicWhere <model> [where]

Never loses data.

Example: smithable op setPublicWhere Incident "status=identified | monitoring | resolved"

Look

addTheme

Add a theme of the project's own (themes/.yml), and use it with --use.

smithable op addTheme <name> [--text …] [--use …]

Never loses data.

setTheme

Choose the theme preset, brand color, background color, font, density or motion ("none" removes a color or the font; for motion it means no motion).

smithable op setTheme [--preset …] [--primary …] [--background …] [--font …] [--density …] [--motion …] [--page …]

Never loses data.

Example: smithable op setTheme --primary "#0f766e"

setSectionLook

Choose how a landing section looks, e.g. hero layout split|centered|plain, faq layout accordion|list ("default" removes it).

smithable op setSectionLook <page> <section> <key> <value>

Never loses data.

setColumns

Choose which fields a page's table shows, in order (none: back to the default).

smithable op setColumns <page> [columns]

Never loses data.

setFieldOrder

Order the fields of a page's form; fields not listed follow (none: the spec's order).

smithable op setFieldOrder <page> [fields]

Never loses data.

setTone

Give a choice value a color by meaning, e.g. paid: success, overdue: warning (neutral: the default).

smithable op setTone <model> <field> <value> <tone>

Never loses data.

setIcon

Choose the icon a page has in navigation ("auto": chosen from its model's name).

smithable op setIcon <page> <icon>

Never loses data.

setBelow

Show a second field under a table column's value, e.g. phone under email ("none" removes it).

smithable op setBelow <page> <column> <field>

Never loses data.

setControl

How a field is entered on a page's form: slider, stepper, radio, select, combobox, textarea or switch ("input" is the default for its kind).

smithable op setControl <page> <field> <control>

Never loses data.

setFormSections

Group a page's form into sections with headings, each "name:field,field"; fields in none follow at the end (none: one form).

smithable op setFormSections <page> [sections]

Never loses data.

setFormColumns

Short fields of a page's form one under the other (1) or two abreast on wide screens (2).

smithable op setFormColumns <page> <columns>

Never loses data.

setView

Show a page's list as a table or as cards on wide screens (a phone shows cards anyway).

smithable op setView <page> <view>

Never loses data.

setSort

The order of a page's list until someone sorts it: a field, or "-field" for the largest or latest first ("none": newest first).

smithable op setSort <page> <field>

Never loses data.

Texts

setText

Change a visible text, e.g. path [pages, customers, subtitle] or [models, Customer, fields, email, label] ("" restores the default).

smithable op setText <text> <path> [--language …]

Never loses data.

Actions and queries

addAction

Add an action: standard with --on and --set (it sets fields of a record), else custom (its behavior is code in src/custom/actions/).

smithable op addAction <action> [input] [--on …] [--when …] [--set …] [--add …] [--copy …] [--with …] [--output …] [--intent …]

Never loses data.

Example: smithable op addAction mergeCustomers keep=Customer! duplicate=Customer!

setAction

Change an action: its intent, the records it is for (--when) or the values it sets (--set). Each replaces what was there; an empty one removes it. Who may run it is grant actions .

smithable op setAction <action> [--intent …] [--when …] [--set …]

Never loses data.

removeAction

Remove an action, from the pages that offer it too. A custom action's code in src/custom/actions/ stays.

smithable op removeAction <action>

Never loses data.

addQuery

Add a custom query: records of a model chosen or ordered by code (e.g. customers most likely to churn). Its code is written afterwards, in src/custom/queries/.

smithable op addQuery <query> <returns> [--intent …]

Never loses data.

Data

importRecords

Add records from a CSV file (a header row; columns matched to fields by name or --map). --dry-run shows the first rows and every problem without adding anything.

smithable op importRecords <model> [--from …] [--text …] [--map …] [--dryRun …]

Never loses data.

Example: smithable op importRecords Customer --from customers.csv

The app as a whole

setRuleId

Give a rule a stable id, so code and tests can say they implement it.

smithable op setRuleId <concept> <number> <id>

Never loses data.

editSpec

Replace smithable.yml, or one of its companion files, with an edited version, as a whole.

smithable op editSpec [text] [--path …] [--value …] [--file …]

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

setPackage

Add a library for custom code at an exact version, e.g. chart.js 4.4.1 ("none" removes it).

smithable op setPackage <name> <version>

Never loses data.

Example: smithable op setPackage chart.js 4.4.1

setApp

Set what the app is: its name, a one-sentence description, its currency (ISO 4217) or time zone ("none" removes the currency or time zone).

smithable op setApp [--name …] [--description …] [--currency …] [--timezone …]

Never loses data.

Other changes

setOwner

Say whose a record is: the user in one of its User fields (the account manager, the salesperson who handles it) instead of whoever created it. Without a field, the creator again. "The owner" in the access rules is then that user.

smithable op setOwner <model> [field]

Never loses data.

setFrame

Which frame a page shows in: site (the public frame with the top menu), app (the workspace with the side menu) or none (the page alone); default returns to its kind's.

smithable op setFrame <page> <frame>

Never loses data.

Example: smithable op setFrame about site

setRecordList

Show a list of related records on a page's record as a thread (a conversation with a reply box) or a table (the default).

smithable op setRecordList <page> <field> <look>

Never loses data.

Example: smithable op setRecordList customers notes thread

setRecordLines

What a page shows under a record's name on cards and in pickers (docs/design/record-names.md §3): a subtitle and detail parts, each a field, a reference or a field one reference away; "none" takes the subtitle away, an empty detail list the details.

smithable op setRecordLines <page> [subtitle] [--detail …]

Never loses data.

Example: smithable op setRecordLines sessions instructor.name --detail classType.level

setAiBadge

Mark pictures an AI made with "AI-generated" or "AI" (true, the default) or not (false): whether your audience needs it is your responsibility.

smithable op setAiBadge [shown]

Never loses data.

Example: smithable op setAiBadge false

setAssistantVoice

Set the manner of Ask, the assistant in the app: plain (the default: short, no pleasantries), friendly or formal.

smithable op setAssistantVoice <voice>

Never loses data.

Example: smithable op setAssistantVoice friendly

setFiltersShown

Which of a page's filters show when the list is first reached; the rest fold behind "More filters" (no fields: every filter shows).

smithable op setFiltersShown <page> [fields]

Never loses data.

Example: smithable op setFiltersShown customers segment creditLimit

setTexts

Change several visible texts at once, each by its path in dots, e.g. { "pages.customers.title": "Kunder" }; with language, in that language file. A key with a dot in it (a choice value like "e.g.") cannot be reached this way: use setText.

smithable op setTexts [--texts …] [--language …]

Never loses data.

nameRecords

Call a kind of record by other names, for one and for several, and retitle its list pages that carried the old name.

smithable op nameRecords <model> <one> [several]

Never loses data.

setLanguage

The language of everything a person reads in the app: en, sv.

smithable op setLanguage <language>

Never loses data.

Example: smithable op setLanguage sv

addLanguage

Offer the app in another language too, with its own file of the app's texts to fill in: en, sv.

smithable op addLanguage <language>

Never loses data.

Example: smithable op addLanguage sv

removeLanguage

Stop offering the app in one of its other languages; its file of texts goes too.

smithable op removeLanguage <language>

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

Example: smithable op removeLanguage sv

The app's mark, a file under public/images/ shown before its name and as its tab icon ("" removes it).

smithable op setLogo [src] [--alt …]

Never loses data.

Example: smithable op setLogo images/logo.webp --alt "A lotus"

setAssistantTexts

Set the texts of Ask, the assistant in the app: its name ("Ask" by default), the first line it shows, the question box's placeholder, and one sentence of what it is for (shown and given to the model); "" clears one.

smithable op setAssistantTexts [--name …] [--greeting …] [--placeholder …] [--about …]

Never loses data.

setDomains

Let only addresses on these email domains sign up (allowedDomains, e.g. acme.com); none (or no domains) lifts it. The admins may always sign in.

smithable op setDomains <domains>

Never loses data.

Example: smithable op setDomains acme.com

setAdmins

Say who the administrators are, by email address: they become admins when they first sign in, and may always sign in.

smithable op setAdmins <admins>

Never loses data.

Example: smithable op setAdmins you@your-domain

setProviders

Choose how people sign in, in button order: email (a code by mail, needs only the app's mail settings), google or facebook (each needs its keys in the environment, see the README).

smithable op setProviders <providers>

Never loses data.

Example: smithable op setProviders email google

setApi

Say what other programs may reach with a key: models ("all" for every model, also later ones), the standard actions they may run, and whether AI assistants may use it too. No models turns the API off. A key's role still decides what it may do.

smithable op setApi [models] [--actions …] [--except …] [--assistants …] [--assistantsDelete …]

Never loses data.

Example: smithable op setApi Customer Order --actions markPaid

setAssistant

Say who gets "Ask", the assistant in the app: some roles, signed-in (everyone who signs in) or anyone (visitors too, who are answered without a model); and whether it only reads or may also change things, each change confirmed by the person. Nobody turns it off. It never deletes.

smithable op setAssistant [for] [--may …]

Never loses data.

setAnalytics

Count visits with plausible, umami, fathom, google and the app's id there ("none" stops it); google asks visitors for consent first.

smithable op setAnalytics <provider> [id]

Never loses data.

Example: smithable op setAnalytics plausible shop.example.com

addTimer

Add a timer: an event the app raises at a time, on a clock (every day at 08:00, or cron) or relative to a date of a record (1 day before session.startsAt), running an action over the records it is due for; outgoing may listen to it.

smithable op addTimer <name> [--on …] [--at …] [--every …] [--cron …] [--where …] [--run …]

Never loses data.

setTimer

Change a timer: its records, time, condition or action. "none" takes a key away (where, run, on); a new time form replaces the old.

smithable op setTimer <name> [--on …] [--at …] [--every …] [--cron …] [--where …] [--run …]

Never loses data.

removeTimer

Remove a timer. An outgoing entry that listens to it is removed first.

smithable op removeTimer <name>

Can lose data in some cases (removing or narrowing what holds records); then it asks first, or takes --yes.

addOutgoing

Tell someone when something happens to a record: mail the record's owner, a role or an address on the record when a record is added, changed or deleted, or an action runs (e.g. a confirmation when a booking is made).

smithable op addOutgoing <outgoing> <when> [--where …] [--fields …] [--to …] [--recipient …]

Never loses data.

Example: smithable op addOutgoing bookingMade "Booking created" --recipient owner

removeOutgoing

Stop telling someone when something happens: remove an outgoing mail or call.

smithable op removeOutgoing <outgoing>

Never loses data.

addIncoming

Add an address other services call (/incoming/, with its own secret): one record per call (--create Model --set field=value, a value may take {path} from the call's JSON, like "{check.name}"), or --custom to hand the call to code. --signed checks a sha256 signature of the body instead of the secret itself.

smithable op addIncoming <name> [--create …] [--set …] [--custom …] [--signed …] [--as …] [--mail …] [--fields …] [--reply …] [--replyFields …] [--replySet …] [--replyThen …]

Never loses data.

setIncomingReply

Say what a reply to one of an incoming mail's records becomes (--create Message --fields from=from body=body, --set on it, --then on the record it joins), or --none to make every mail a new record again.

smithable op setIncomingReply <name> [--create …] [--fields …] [--set …] [--then …] [--none …]

Never loses data.

removeIncoming

Remove an incoming call: its address answers no more. Custom code for it stays.

smithable op removeIncoming <name>

Never loses data.