Extend Bolt

Your first Bolt App in 5 minutes.

Pick the track that fits you. A snippet needs nothing but Bolt. A User App needs the desktop app. A connector needs admin rights in your organization's Bolt.

Track A, anyone, tier 1

A snippet that cleans a list.

You have a messy list like pear, apple, kiwi, apple. You want one item per line, sorted, no repeats.

  1. Open Settings → Search & Commands → Snippets and click Add snippet.
  2. Key: cleanlist. Label: Clean a list. Content: the line below. Save.
  3. Copy a comma-separated list. Open Bolt, type ;cleanlist, press Enter.
  4. Paste. You get apple, kiwi, pear, one per line.
Snippet content
{clipboard|splitcsv|sort|uniq}

{clipboard} is what you copied; each | is a filter. More filters, fill-in fields and pipelines are on the Snippets page.

Track B, anyone on the desktop app

A User App, in one YAML file.

A User App is a YAML file in a folder on your computer. Bolt reads it and gives you a new = key. This one is tier 2: it only reshapes text you paste, with no command and no network.

  1. Open Settings → Desktop App → User Apps. Check that Allow User Apps on this device is on, then click Open folder.
  2. Save the YAML below in that folder as my-scrub.yaml.
  3. Back in Settings, click Reload. If the file has a mistake, the reason is shown under the list.
  4. Paste text that holds an SSN or an email address into the Bolt launcher. A Scrub IDs row offers the masked version. You can also type =my-scrub and your text.
my-scrub.yaml
schema_version: 1
runtime: transform
id: my-scrub
chip: my-scrub
title: Scrub IDs
icon: shield-off
description: Mask SSNs and email addresses in pasted text, on this device
placeholder_examples: ["SSN 123-45-6789 email [email protected]"]
emits: Generic

recognize:
  patterns:
    - regex: '\b\d{3}-\d{2}-\d{4}\b'
    - regex: '[\w.+-]+@[\w-]+\.[\w.-]+'
  confidence: ambient

transform:
  pipe:
    - replace: { from: '\b\d{3}-\d{2}-\d{4}\b', to: '[SSN]' }
    - replace: { from: '[\w.+-]+@[\w-]+\.[\w.-]+', to: '[email]' }
    - sanitize

presentation:
  widget: list
  title_field: line

Next: run a command (tier 3)

A bridge App runs one command and shows each line of output as a row. Bolt runs it as a single program call, never through a shell, so pipes and ; are refused. It runs with your own rights, the same as typing it in a terminal, and only after you press Enter.

my-git-log.yaml
schema_version: 1
runtime: bridge
id: my-git-log
chip: my-git-log
title: Recent commits
icon: git-commit
description: Recent commits of a repo under ~/dev
placeholder_examples: ["my-app", "infra 30"]
emits: Generic

detect:
  binary: git

bridge:
  command:
    macos:   "git -C {{home}}/dev/{{repo}} log --oneline -{{limit}}"
    linux:   "git -C {{home}}/dev/{{repo}} log --oneline -{{limit}}"
    windows: "git -C {{home}}/dev/{{repo}} log --oneline -{{limit}}"
  parse: lines
  timeout_seconds: 5

args:
  - { name: repo,  from: "query.word(0)" }
  - { name: limit, from: "query.word(1)", default: 20 }

presentation:
  widget: list
  title_field: line

Type =my-git-log my-app 10. {{home}} is your home folder; a ~ would not work, because no shell expands it. detect hides the key on a computer without git. The full field list is on the User Apps page.

Track C, admins

A connector for everyone in your organization.

A connector is a manifest your admin adds once, and everyone in the organization gets the = key. It runs in Bolt's service (your organization's server, or the one inside the desktop app), with each person's own sign-in. These two call public APIs that need no sign-in.

  1. Open Settings → Admin → Utilities and choose the Custom tab.
  2. Paste the manifest. Click Preview to check it, then Save manifest.
  3. In Bolt, type =worldclock Asia/Kolkata and press Enter. You get one row with the current time.
=worldclock
schema_version: 1
id: worldclock
chip: =worldclock
title: World Clock
description: Current time for an IANA timezone.
emits: Generic

scopes:
  - id: lookup
    label: Lookup zone
    default: true
    flow:
      - call:
          kind: http
          method: GET
          url: "https://timeapi.io/api/Time/current/zone"
          query:
            timeZone: "{{query | default:'America/New_York'}}"
        out: r
      - emit: r

presentation:
  widget: list
  title_field: dateTime
  subtitle_field: timeZone
  placeholder: "Try America/New_York or Asia/Kolkata"

scopes are sub-commands; the default: true one runs when someone types the key. A flow is a list of steps: call fetches, emit returns the rows. {{query}} is what the person typed after the key.

A five-vendor status board

This one calls five status feeds at once with parallel, shapes them into one list with a jq transform, and puts the vendor with open incidents first. Each row has an Open status page action.

=status
schema_version: 1
id: status
chip: =status
title: SaaS status board
description: Live status of five vendors from their public Statuspage feeds.
emits: Generic

scopes:
  - id: board
    label: Status board
    default: true
    flow:
      - parallel:
          - call: { kind: http, method: GET, url: "https://www.githubstatus.com/api/v2/summary.json" }
            out: github
          - call: { kind: http, method: GET, url: "https://www.stripestatus.com/api/v2/summary.json" }
            out: stripe
          - call: { kind: http, method: GET, url: "https://www.cloudflarestatus.com/api/v2/summary.json" }
            out: cloudflare
          - call: { kind: http, method: GET, url: "https://discordstatus.com/api/v2/summary.json" }
            out: discord
          - call: { kind: http, method: GET, url: "https://status.openai.com/api/v2/summary.json" }
            out: openai
      - transform: |
          [
            { vendor: "GitHub",     description: .github.status.description,     incidents: (.github.incidents     | length), url: .github.page.url },
            { vendor: "Stripe",     description: .stripe.status.description,     incidents: (.stripe.incidents     | length), url: .stripe.page.url },
            { vendor: "Cloudflare", description: .cloudflare.status.description, incidents: (.cloudflare.incidents | length), url: .cloudflare.page.url },
            { vendor: "Discord",    description: .discord.status.description,    incidents: (.discord.incidents    | length), url: .discord.page.url },
            { vendor: "OpenAI",     description: .openai.status.description,     incidents: (.openai.incidents     | length), url: .openai.page.url }
          ] | sort_by(.incidents) | reverse
        out: rows
      - emit: rows

actions:
  - id: open
    kind: url
    target: row
    label: "Open status page"
    url_template: "{{row.url}}"

presentation:
  widget: list
  title_field: vendor
  subtitle_field: description
  list_fields: [vendor, description, incidents]
  row_key_field: vendor

Next

Share it.

A User App can travel as a Boltpack. Skills and agents can be exported as one from Settings. The public gallery is coming; see Publishing.