#Phrases & Matching

Phrases are how the user's typed text finds your action. Getting a feel for the matcher will save you a lot of "why didn't that trigger?" moments.

#Step 1: tokenizing

Before anything is compared, both the user's input and your phrases are tokenized. Tokenizing:

  • lowercases the text,
  • strips the punctuation . , ! ? ; :,
  • and splits on whitespace into a list of words.

So Set Volume to 40! and set volume to 40 both become the same tokens: ["set", "volume", "to", "40"]. You don't need to worry about capitalization or trailing punctuation in your phrases — but curly braces are meaningful and are not stripped.

#Step 2: matching

A phrase is a sequence of literal words and {placeholders}. Matching walks the phrase and the input together:

  • A literal word must appear in the input, in order. If it's missing, the phrase doesn't match at all.
  • A placeholder captures words from the input into a parameter.

How much a placeholder captures depends on where it sits:

  • A placeholder in the middle captures everything up to the next literal word in the phrase. open {app} now matched against open google chrome now captures app = "google chrome". If that next literal (now) never appears, the phrase doesn't match.
  • A placeholder at the end is greedy — it captures all remaining words. open {app} against open google chrome captures app = "google chrome".

#Examples

Phrase Input Captured params
hello hello (none)
set volume to {target} set volume to 40 target = "40"
open {app} open google chrome app = "google chrome"
email {who} about {what} email sam about lunch who = "sam", what = "lunch"
close {app} now close spotify (no match — now is missing)

#Step 3: picking a winner

Several phrases across several packs can match the same input. When that happens, Personal Goober prefers the most specific match — the one with the fewest placeholders. A phrase made entirely of literal words beats one with a placeholder.

That means you can safely register both a general phrase and specific shortcuts:

json
{
    "playMusic": ["play {query}"],
    "playFavorites": ["play my favorites"]
}

Typing play my favorites matches both play {query} (with query = "my favorites") and the literal play my favorites — and the literal one wins because it has zero placeholders.

#Tips & gotchas

  • Put a literal after a mid-phrase placeholder. A placeholder needs a following literal word to know where to stop. If you want to capture "the rest of the sentence," make the placeholder the last token.
  • Avoid two placeholders in a row. {a} {b} has no literal between them to split on. Separate them with a word: from {a} to {b}.
  • List natural variations. Give each action several phrasings (set volume to {x}, change volume to {x}, turn the volume to {x}) so real users actually hit them.
  • Case and punctuation don't matter — they're normalized away — so don't rely on them to disambiguate.

Next: Packaging & Distributing to get your pack onto other people's machines.