evoke Get started

Examples · 02 of 06

One sentence finds an address and starts a draft with it.

Two reflexes connect. What contact finds, mail takes, because one declares what its result holds and the other lacks a value of that kind. A recorded session from the product's own tests, and a pattern for your app.

The request and the result

01

Two steps, one sentence.

Each part is decided on its own, in the order you typed it. The plan shows first. The stage plays three sentences in turn: one that ran, one declined at its first step, one refused.

~/home
$ evoke "look up dana's address and email them" 1 contact name="dana" 0.90 2 mail · takes email from 1 dana <dana@example.com> 2 mail to="dana@example.com" 0.88 drafted to dana@example.com
  1. 1«them» names step 1. mail needed an address. contact declares one under [yields], and the plan takes it: takes email from 1.
  2. 2A no ends the sentence at that step. In the second sentence, a timer asked at its turn. Declined, and the lights step after it was skipped. The exit code is the worst step's.
  3. 3A part that matches nothing refuses the whole sentence, before anything runs. Nothing is left half done.

The operations

02

Two manifests, and the test that pins them.

These files come from the product's own test home. The contact body is a stand-in that makes an address from the name. In yours, it would ask your directory.

contact/reflex.tomlthe author's
reflex = 1

description = """
Look up a person's email address in the team directory.
One person at a time; prints the address."""

not_for = ["writing to anyone", "phone numbers", "adding a person to the directory"]
tags    = ["people"]
effect  = "read"
confirm = "Look up {name}'s address?"
run     = "contact.mts"

[args.name]
ask   = "Whose address?"
vocab = "people"

[yields]
email = "email"

[examples]
"look up dana's address"    = {}
"what is sam's email"       = {}
"find the address for dana" = {}

[tests]
"dana's email please" = {}
"email dana"          = false
contact/contact.mtsa stand-in
export default async ({ name }: { name: string }) => {
  const email = `${name}@example.com`
  return { text: `${name} <${email}>`, data: { email } }
}
vocab/people.tomlyours
dana = "Dana Ortiz, operations."
sam  = "Sam Lee, sales."
  1. 1The handoff is in the manifests. [yields] email = "email" on one side, pick = "email" for to on the other. Same kind, so it binds.
  2. 2The lookup data is a vocabulary. dana and sam are words you filled. The body turns the word into an address. Yours would query a directory.
  3. 3The test replays a recording. No key, no network. It pins the bind, the ask it fills, the call, and both results.

One boundary

03

A reference that takes nothing asks before anything runs.

«for them» points at step 1, but a timer needs no address. evoke says so, and asks whether to run the plan as it stands. Nothing is guessed.

evoke
$ evoke "look up dana's address and start a 10 minute timer for them"
  1  contact name="dana"  0.90
  2  timer duration="10 minute"  0.88 · after 1
  step 2 refers to step 1, but takes nothing from it
  Run the plan as it stands?  [y]es [n]o > y
dana <dana@example.com>
10 minute timer started

The same holds for a reference two fields could satisfy: it stops with a line naming them. What a step takes, in the manual →

Before you run it

04

What this pattern needs.