Your Mac now has a free, offline language model you can pipe text through, and most developers have not typed the two letters yet. The fm command ships preinstalled with macOS 27 as a terminal front end to Apple's Foundation Models: the same on-device small model that powers Apple Intelligence, with an opt-in path to the larger Private Cloud Compute model. It needs no account, no API key and charges nothing per token. Enable it once with sudo fm license, then fm chat gives you a conversation with slash commands and fm respond writes a single answer to stdout, which is the mode that matters for scripts. It also speaks structured JSON, keeps transcripts, reports token counts and can run an OpenAI-compatible REST server on localhost.
Short answer: fm is the right tool for small, private, zero-cost text transforms inside shell pipelines: summarising a diff, classifying a log line, drafting a commit message, extracting fields to JSON. It is the wrong tool for long documents, code generation at scale and anything that needs a frontier model's judgement. Below are the five recipes we kept after a week, the failure table, and the decision rule between fm, mlx_lm and Ollama.
Setup in ninety seconds
# one-time
sudo fm license
# sanity check
fm respond "Reply with the single word READY."
# structured output
fm respond --json '{"type":"object","properties":{"lang":{"type":"string"},"score":{"type":"number"}}}' "Detect the language of: Bonjour tout le monde"
# local OpenAI-compatible server for tools that expect one
fm serve --port 8734
Flag names follow the WWDC26 session "Build AI-powered scripts with the fm CLI and Python SDK." If a flag differs on your build, fm --help is authoritative; Apple has iterated the surface since the beta and third-party write-ups lag it.
Five recipes worth keeping
1. Commit message from a staged diff
gitmsg() {
git diff --cached | head -c 12000 | fm respond "Write a conventional commit message for this diff. First line under 72 chars, imperative mood, then a blank line and up to 4 bullet points. Output only the message."
}
The head -c matters. The on-device context is small; feed it the first 12 KB and let it summarise, or it will truncate silently and describe half the change.
2. Log triage to JSON
tail -n 200 /var/log/app.log | fm respond --json '{"type":"object","properties":{"errors":{"type":"integer"},"top_causes":{"type":"array","items":{"type":"string"}},"needs_human":{"type":"boolean"}}}' "Classify these log lines. Count ERROR lines, list up to 3 distinct causes, set needs_human if any cause mentions data loss or auth."
Structured output is where fm earns its place. A JSON schema turns a chatty model into a function you can jq.
3. Rename files by content
for f in ~/Downloads/*.pdf; do
name=$(pdftotext -l 1 "$f" - 2>/dev/null | head -c 4000 | fm respond "Give a 6-word kebab-case filename for this document. Output only the filename, no extension.")
[ -n "$name" ] && mv "$f" ~/Downloads/"$name".pdf
done
4. Meeting notes to actions
pbpaste | fm respond "Extract action items as a markdown checklist. Each item: owner in bold, task, due date if stated. Nothing else." | pbcopy
Clipboard in, clipboard out. This is the recipe that gets used ten times a day once it exists.
5. Pre-filter before an expensive model
is_worth_reading() {
fm respond --json '{"type":"object","properties":{"relevant":{"type":"boolean"}}}' "Is this article about $1? Answer strictly." <<< "$(cat)" | jq -r .relevant
}
# use: curl -s "$url" | html2text | is_worth_reading "Next.js caching"
The pattern that saves the most money: let the free local model say no, and send only the yeses to the model you pay for. Our token counter and token estimator will show you how much of a paid budget a filter like this protects.
Where it fails
| Task | What happens | Use instead |
|---|
| Documents over a few thousand words | Silent truncation; the answer describes the first part | Chunk and map-reduce, or a larger local model via mlx_lm |
| Multi-file code generation | Plausible but shallow; misses cross-file contracts | Claude Code or another frontier coding agent |
| Facts after the model's training window | Confident, wrong | Anything with retrieval |
| Long-form writing with voice | Generic register, repetitive structure | Frontier model plus a style lock |
| Non-English nuance beyond major languages | Uneven; fine for classification, weak for generation | Larger open-weights model |
| Strict schema with deep nesting | Occasional malformed field on complex schemas | Flatten the schema; validate with jq before use |
None of these are surprising for a small on-device model. The mistake is expecting a frontier model because the interface looks like one.
The three-tool decision rule
fmwhen the input is under a few thousand words, privacy matters, the output is a classification, extraction or short draft, and zero cost per call is the point. It is also the only one of the three that needs no install.mlx_lmwhen you want a specific open-weights model running natively on Apple silicon, longer context, or reproducible results pinned to a model file. It is a Python dependency and a download, and it is worth both for anything serious.Ollama when you need the same model on a Linux box tomorrow, or a tool ecosystem that already speaks the Ollama API. Apple's
fm serveexposes an OpenAI-compatible endpoint, which closes some of that gap, but Ollama's catalogue and portability are still the reason to keep it.
Most shell work lands in the first bucket. The self-hosting logic behind the second and third is covered in the self-hosting stack for 2026.
Using fm from an agent
Because fm respond is a plain command, coding agents can call it as a tool. We wrapped the pre-filter recipe as a Claude Code skill so that a research task runs the free local classifier over fetched pages before the paid model reads any of them. The skill anatomy is in Agent skills beat agent crews, and the pattern of a cheap gate in front of an expensive worker is the same one that drives the cost rules in the Claude Code Production Pack. If you keep a notes vault, the Obsidian developer productivity vault has a shell-commands section where these functions belong. For the Claude Code settings that pair with local pre-filters, see the August to September settings roundup.
Privacy, cost and the PCC switch
The default model runs entirely on the Mac. Inside fm chat, /model can switch to the Private Cloud Compute model for harder prompts; that request leaves the machine to Apple's attested servers, which is still a stronger privacy posture than most hosted APIs but is no longer "offline." For scripts that must never leave the device, stay on the default and say so in the script header so a future you does not flip the switch for speed.
Benchmark it on your own machine before you trust it
Numbers from someone else's Mac are not your numbers, and the on-device model's speed depends on chip generation, memory pressure and what else is running. Measure before you wire fm into anything that matters. The script below records latency and output size for a fixed prompt set so you can compare across machines, after OS updates, and against mlx_lm on the same tasks.
#!/bin/zsh
# fm-bench.sh — run a fixed prompt set, log wall time and output length
prompts=(
"Summarise in one sentence: $(head -c 3000 README.md)"
"Return JSON with keys lang,score for: Guten Morgen"
"Write a conventional commit for: fix null check in parser"
)
for p in $prompts; do
start=$(date +%s.%N)
out=$(fm respond "$p")
end=$(date +%s.%N)
secs=$(echo "$end - $start" | bc)
chars=$(printf '%s' "$out" | wc -c | tr -d ' ')
label=$(printf '%.40s' "$p")
printf '%.2fs %s chars %s
' "$secs" "$chars" "$label"
done
Run it three times cold and three times warm; the first call after a while pays a model-load cost that later calls do not. Record the median, not the best. Then run the same three prompts through your paid API and through an mlx_lm model of similar size. The comparison you want is not "which is fastest" but "which is fast enough to sit inside a keystroke," because that is the threshold at which a shell function gets used.
Turning recipes into a shell library
Recipes typed once are recipes forgotten. Put the five functions in a single file, source it from your shell profile, and give each a one-line comment that doubles as its help text. Keep the prompts inside the functions rather than in separate files; the point of fm is that the whole tool fits in a place you can read. Add a guard at the top of the file that checks fm exists and returns quietly if not, so the same profile works on a Linux box. Version the file in your dotfiles, and when Apple changes a flag, you fix it in one place. Finally, resist the urge to add a sixth function on day one. The five above survived a week because they replaced something you already did by hand; a sixth that solves a problem you do not have will be the first thing you stop maintaining.
One-liners worth adapting
Once the five functions exist, the same shape covers a surprising amount of daily friction. Each of these is a single fm respond call with a strict instruction and, where the output feeds another command, a JSON schema.
Explain a cryptic error: pipe the last thirty lines of a failing build into fm with "Explain the root cause in two sentences and name the file to open first." It is wrong often enough that you still read the log, and right often enough that you read it faster.
Draft a pull request description from the branch's commit list, then edit. The model is good at turning ten commit lines into three paragraphs and poor at knowing which change mattered; you supply that.
Normalise messy CSV headers to snake_case before importing into a spreadsheet or a tool such as our JSON to CSV converter.
Classify inbox exports by intent (invoice, support, spam, personal) into JSON, then let a script move the files. Keep the schema flat; nested objects are where the small model slips.
Rewrite a paragraph at a fixed reading level for documentation, with the instruction to keep every number and every proper noun unchanged. Check the numbers anyway.
The pattern behind all of them is the same as the pre-filter recipe: a cheap, private, local model does the first pass, a human or a paid model does the judgement, and nothing leaves the machine unless you decide it should. That division is what makes a two-letter command worth a place in your shell profile.
Quick answers
Is fm available on macOS 26?
Community write-ups report fm running on macOS 26 as well. It is preinstalled with macOS 27; run fm --help to confirm what your build exposes.
Does fm cost anything or need an account?
No. It runs the on-device Apple Intelligence model with no account, API key or per-token charge. Enable it once with sudo fm license.
Can fm load other models like Llama or Qwen?
Not through fm itself. Use mlx_lm for open-weights models on Apple silicon, or Ollama for portability across machines.
How big an input can fm handle?
Small. Treat a few thousand words as the ceiling, feed it the head of a document, and chunk anything longer.
Can other tools talk to fm?
Yes. fm serve exposes an OpenAI-compatible local endpoint, so tools that accept a base URL can point at it.
Two letters, zero dollars, five recipes. Every product mentioned is available at wowhow.cloud — pay once, ship forever.
Originally published at wowhow.cloud
Top comments (0)