Presets

Saved command bundles, available across every verb.

Use presets to save provider, model, and verb-specific flags under a name. Presets exist for every verb category — AI generation (text, image, video, speech, transcription), web (search, scrape, answer, map, crawl, research, findall), and data (enrich, lookup, verify).

marmot preset create <name> --mode <mode> [flags…]
marmot preset update <name> [flags…]
marmot preset rename <old> <new>
marmot preset delete <name>
marmot preset list
marmot preset get <name>

marmot @<name> "..."           # sigil — auto-routes to the right verb
marmot search @<name> "..."    # explicit verb still works

For multi-stage workflows that chain several verb invocations together, see Pipelines — they share the same @<name> sigil routing.

The @name sigil

The shorthand @<name> expands to --preset <name>. As of 0.4.7, it also routes to the verb that matches the preset's mode when no explicit verb is given. So a search-mode preset dispatches to marmot search, an enrich-mode preset to marmot enrich, and so on:

marmot preset create linkedin --mode search --provider parallel --include-domains linkedin.com
marmot @linkedin "Daniel Francis Abel Police"   # → marmot search

Mode → verb mapping covers all 15 modes. Three AI exceptions remap because the verb name differs from the mode name: speechspeak, transcriptiontranscribe, text → default-run (no verb token needed).

Explicit verbs still win — marmot scrape @some-search-preset url keeps scrape as the verb and surfaces a clean mode-mismatch error rather than silently swapping. Only the first matching @… token is consumed, so marmot run "@user said hi" keeps the literal in the prompt.

Identity: every preset has a stable preset_id

As of 0.6.0, each preset carries a stable preset_id UUID assigned at creation. Sessions and the privacy-safe usage log reference presets by preset_id rather than by slug. Renaming a preset is therefore a no-op at the data layer — sessions and historical usage records keep working without any rewrites.

The preset_id is auto-generated; users only ever interact with the slug. Display surfaces (marmot session get, marmot session list, chat-mode export) resolve preset_id → current slug at render time. Orphan ids (the preset was deleted) render as the raw UUID or (deleted).

Rename

marmot preset rename <old> <new>

Validates that <new> follows the slug regex (^[a-z0-9]+([-_][a-z0-9]+)*$), is not already taken, and that <old> exists. Atomic config rewrite. Output:

{
  "ok": true,
  "action": "rename",
  "from": "linkedin-people",
  "to": "linkedin-folks",
  "preset_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Because the preset_id is stable, sessions that bound to the old slug continue to resolve correctly; the next marmot session get displays the new slug. Historical usage records keep their original preset_id and resolve to the new slug at display time.

Create

marmot preset create <name> --mode <mode> [flags…]   # flag-driven
marmot preset create                                  # interactive walkthrough
marmot preset create <name>                           # interactive, name pre-filled

Names follow the slug regex ^[a-z0-9]+([-_][a-z0-9]+)*$ — lowercase letters and digits with single - or _ separators (no leading, trailing, or consecutive separators).

Update

marmot preset update <name> [flags…]   # flag-driven (patches the supplied fields)
marmot preset update <name>            # interactive walkthrough — current values shown as defaults

Patches the supplied flags onto an existing preset. A preset's mode is fixed; delete and recreate it if the mode needs to change.

Interactive walkthrough

Run marmot preset create (no args) or marmot preset create <name> (name only) to enter an interactive walk that prompts for each preset-able field. Same for marmot preset update <name> with no field flags.

The walk:

  1. Name — only on create, only if not passed positionally. Validates the slug regex on the fly; re-prompts on invalid.

  2. Mode — only on create. Pick from the 15 modes; render shows the verb each mode dispatches to (text → marmot run, speech → marmot speak, …).

  3. Provider, model, retries, timeout — common to every mode. Skip any to use the configured default. The model picker is searchable: alphabetized, shows a scrollable window of 10 entries, and accepts typed input to filter by lowercase substring match across model id and label. Pick Other / type a custom value to enter a model id that isn't in the cached list.

  4. Mode-specific fields — the walker iterates the field list for the chosen mode in declaration order. Each field type has its own prompt:

    Field typePrompt
    string / path / numbertext input. On update, the current value is pre-filled — press Enter to keep it, or edit in place to change. On create, empty input = skip.
    boolthree-way select: enable / disable / skip-or-keep
    enumselect from the enum members
    list (file, image, stop, urls)on create: loop until empty input; on update: Keep / Append / Replace
  5. Mutually-exclusive groups — for fields like text mode's schema | schemaFile | schemaModule, the walker asks which one (if any) once and only walks the chosen branch.

  6. Review — pretty-prints the assembled preset. Confirm to save, or cancel to discard.

The flow only triggers on a TTY. Piping marmot preset create < /dev/null or running it in a non-interactive environment errors clearly: pass --mode and field flags directly instead.

$ marmot preset create
  Preset name
 tech-search

  Mode (which verb is this preset for?)
 search marmot search

  Provider
 parallel

  Save this preset?
 Yes

  Saved preset "tech-search".

Modes and their preset-shaped flags

Common to every mode: --provider, --retries, --timeout. AI modes also accept --model. Web and data modes do not — provider implies the API surface.

AI modes

ModeVerbMode-specific flags
text(default)--prompt, --prompt-file, --system, --system-file, --schema, --schema-file, --schema-module, --file (list), --image (list), --temperature, --max-tokens, --top-p, --seed, --stop (list), --reasoning, --provider-option, --output, --stream, --text, --json, --session
imageimage--prompt, --prompt-file, --size, --quality, --style, --negative, --seed, --n, --provider-option, --output, --binary, --b64, --json, --preview, --session
videovideo--prompt, --prompt-file, --image (list), --aspect, --resolution, --duration, --fps, --audio / --no-audio, --n, --seed, --provider-option, --output, --binary, --b64, --json, --session
speechspeak--text (positional), --prompt-file, --voice, --format, --speed, --instructions, --provider-option, --output, --binary, --b64, --json, --play, --wait, --session
transcriptiontranscribe--audio (positional), --language, --format, --prompt (concat), --provider-option, --output, --text, --json, --session

Web modes

ModeVerbMode-specific flags
searchsearch--query, --limit, --depth, --freshness, --after-date, --before-date, --include-domains, --exclude-domains, --include-content, --cache, --refresh, --output, --raw, --session
scrapescrape--urls (list), --format, --query, --cache, --refresh, --output, --raw, --session
answeranswer--query, --max-citations, --include-search, --cache, --refresh, --output, --raw, --session
mapmap--url, --search, --limit, --cache, --refresh, --output, --raw, --session
crawlcrawl--url, --max-pages, --max-depth, --instructions (concat), --include-paths, --exclude-paths, --allow-external, --wait, --async, --output, --raw, --session
researchresearch--query, --depth, --schema, --schema-file, --instructions (concat), --wait, --async, --poll-interval, --max-wait, --output, --raw, --session
findallfindall--objective, --limit, --schema, --schema-file, --entity-type, --match-conditions, --wait, --async, --output, --raw, --session

Data modes

ModeVerbMode-specific flags
enrichenrich--type (person / org), --email, --email-hash, --linkedin, --phone, --name, --first-name, --last-name, --middle-name, --company, --provider-id, --domain, --website, --ticker, --min-likelihood, --require, --fields, --cache, --refresh, --output, --raw, --session
lookuplookup--type (person / org / email), --q, --limit, --cursor, --title, --seniority, --location, --domain, --industry, --employees, --tech, --email-type, --department, --company, --cache, --refresh, --output, --raw, --session
verifyverify--email, --cache, --refresh, --output, --raw, --session

As of 0.7.0 every data-verb identifier is preset-able — the realistic use is partial baking (e.g. --company acme.com in a preset, --first-name Jane at runtime). All identifier fields are scalar-replace: a runtime flag fully replaces a preset value.

Use

marmot search --preset <name> "..."
marmot @<name> "..."

Explicit flags interact with preset values via three rules — see Merge rules below for details. In short: scalars replace, lists append, prompt-like text concatenates. So this call uses the saved provider but bumps the limit:

marmot @linkedin "Daniel Francis Abel Police" --limit 50

Merge rules

When both a preset and a runtime flag set the same field, the value is merged according to the field's shape:

ShapeBehaviorExamples
ScalarRuntime flag replaces preset value.--provider, --model, --temperature, --seed, --stream (all booleans).
ListPreset list followed by runtime list (append).--file, --image, --stop.
Prompt-like textJoined with \n\n so preset + runtime compose.--system, positional prompt, preset's prompt field.

Two intentional asymmetries:

  • --provider-option is list-shaped (key=value repeatable) but stays as scalar replace — the runtime list replaces the preset list entirely. Append would silently produce duplicate keys.
  • For a preset boolean set to true, the matching --no-X runtime flag flips it to false. (E.g. --no-stream overrides a preset's stream: true.) For positive overrides of --no-X-only flags like --no-cache, web/data verbs accept a --cache partner.

Permanent exclusions

A preset cannot set:

  • --api-key — security; provider keys live in ~/.marmot/config.json under providers.<slug>.apiKeyEnvVar, not in presets.
  • --preset — recursion would let a preset apply itself.
  • Stdin-only modifiers (--file-mime, --image-mime, --text-stdin) — they only have meaning when stdin is piped at runtime.

Schemas validate this with strict mode: writing one of these into a preset errors at parse.

Path resolution for preset paths

Preset fields holding filesystem paths (systemFile, promptFile, schemaFile, schemaModule, file, image, output) resolve at use time:

  • Absolute paths used as-is.
  • ~ expanded to the user's home directory.
  • Relative paths resolved against the invocation cwd, not against the config file's directory.

So a preset stored in ~/.marmot/config.json with systemFile: "./prompts/coding.md" looks for ./prompts/coding.md from wherever you ran marmot. For portability, prefer ~/... or absolute paths in global presets.

Examples

# Search preset bound to LinkedIn results via Parallel
marmot preset create linkedin-people \
  --mode search --provider parallel --include-domains linkedin.com --limit 25
marmot @linkedin-people "Daniel Francis Abel Police"

# Deep research with a fixed schema
marmot preset create research-fintech \
  --mode research --provider parallel --depth deep \
  --schema-file ~/schemas/research-output.json
marmot @research-fintech "competitive analysis on stripe vs adyen"

# People enrichment via PDL with a likelihood floor
marmot preset create enrich-people-pdl \
  --mode enrich --provider pdl --type person --min-likelihood 8
marmot @enrich-people-pdl --email tcook@apple.com

# AI image preset
marmot preset create square-1024 \
  --mode image --provider openai --size 1024x1024 --quality hd
marmot @square-1024 "a marmot in the alps"

Delete

marmot preset delete <name>

Returns {removed: false} if the name didn't exist (not an error).

List and show

marmot preset list                # human table on TTY, JSON when piped
marmot preset list --json         # always JSON envelope
marmot preset list --markdown     # markdown pipe-table

marmot preset get <name>         # human key/value sections on TTY, JSON when piped
marmot preset get <name> --json
marmot preset get <name> --markdown

The default output is TTY-aware: a column-aligned table when stdout is an interactive terminal, the JSON envelope (today's structure preserved) when piped or redirected. Pass --json or --markdown to force a specific format. --json and --markdown are mutually exclusive.

Config keys

Presets live under the top-level presets map in ~/.marmot/config.json:

{
  "presets": {
    "linkedin-people": {
      "mode": "search",
      "provider": "parallel",
      "limit": 25,
      "includeDomains": "linkedin.com"
    },
    "research-fintech": {
      "mode": "research",
      "provider": "parallel",
      "depth": "deep",
      "schemaFile": "~/schemas/research-output.json"
    }
  }
}

Presets store flags only, never credentials.