Arthur-Ficial/apfelPublic

The free AI already on your Mac. CLI tool, OpenAI-compatible server, and interactive chat — all on-device via Apple Intelligence. No API keys, no cloud, no downloads.

AI summary: A CLI tool and local OpenAI-compatible server that exposes the built-in Apple Intelligence LLM on macOS natively.

Stars
6.5K
+5 today
Forks
246
Watchers
18
Open issues
16
Open PRs
1
Contributors
~14
Commits
630
Branches
10

SwiftMITCreated Mar 24, 2026Last push 5d agoLatest release v1.12.0+11 stars this week+109 this month

Quick answers

What is apfel?
A CLI tool and local OpenAI-compatible server that exposes the built-in Apple Intelligence LLM on macOS natively.
What does apfel do?
Apfel is a Swift-based utility that unlocks the Apple FoundationModels LLM natively built into Apple Silicon Macs, making it accessible from the command line. It operates entirely on-device without requiring API keys, cloud connectivity, or massive model downloads. The tool functions in three distinct modes: as a scriptable UNIX CLI utility for quick answers, file summarization, and code generation; as a drop-in local OpenAI-compatible HTTP server (`localhost:11434/v1`) for use with standard SDKs; and as an interactive terminal chat REPL. It leverages Apple Intelligence to provide fast, privacy-preserving AI inference directly on local hardware.
Who is apfel for?
Apfel is built for Mac power users, software developers, and sysadmins running macOS 26 or later on Apple Silicon (M1+). It is perfect for those who want command-line AI capabilities without paying for API usage or compromising local data privacy.
How do I get started with apfel?
brew install apfel
How popular is apfel on GitHub?
Arthur-Ficial/apfel has 6,452 stars and 246 forks on GitHub, and gained 11 stars in the last 7 days.
What license does apfel use?
Arthur-Ficial/apfel is released under the MIT license.

Star history

since Jul 28, 2026
02K4K6KJul 2026Aug 2026Sep 2026Oct 2026
6.5K stars as of Oct 3, 2026. Measured daily since Jul 28, 2026; GitHub no longer exposes earlier star timestamps.

Contribution activity

commits per day, last 52 weeks
OctNovDecJanFebMarAprMayJunJulAugSepMonWedFri2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-08: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 0 commits2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 0 commits2026-02-11: 0 commits2026-02-12: 0 commits2026-02-13: 0 commits2026-02-14: 0 commits2026-02-15: 0 commits2026-02-16: 0 commits2026-02-17: 0 commits2026-02-18: 0 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 0 commits2026-02-24: 0 commits2026-02-25: 0 commits2026-02-26: 0 commits2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 0 commits2026-03-02: 0 commits2026-03-03: 0 commits2026-03-04: 0 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 0 commits2026-03-14: 0 commits2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 0 commits2026-03-18: 0 commits2026-03-19: 0 commits2026-03-20: 0 commits2026-03-21: 0 commits2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 5 commits2026-03-25: 20 commits2026-03-26: 29 commits2026-03-27: 2 commits2026-03-28: 0 commits2026-03-29: 0 commits2026-03-30: 0 commits2026-03-31: 37 commits2026-04-01: 3 commits2026-04-02: 8 commits2026-04-03: 16 commits2026-04-04: 16 commits2026-04-05: 27 commits2026-04-06: 8 commits2026-04-07: 10 commits2026-04-08: 24 commits2026-04-09: 0 commits2026-04-10: 27 commits2026-04-11: 13 commits2026-04-12: 23 commits2026-04-13: 6 commits2026-04-14: 3 commits2026-04-15: 9 commits2026-04-16: 9 commits2026-04-17: 5 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 3 commits2026-04-21: 0 commits2026-04-22: 24 commits2026-04-23: 0 commits2026-04-24: 8 commits2026-04-25: 3 commits2026-04-26: 4 commits2026-04-27: 3 commits2026-04-28: 2 commits2026-04-29: 5 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 0 commits2026-05-05: 3 commits2026-05-06: 0 commits2026-05-07: 0 commits2026-05-08: 0 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 0 commits2026-05-12: 0 commits2026-05-13: 0 commits2026-05-14: 9 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 0 commits2026-05-18: 4 commits2026-05-19: 0 commits2026-05-20: 8 commits2026-05-21: 3 commits2026-05-22: 0 commits2026-05-23: 0 commits2026-05-24: 0 commits2026-05-25: 0 commits2026-05-26: 1 commit2026-05-27: 0 commits2026-05-28: 0 commits2026-05-29: 0 commits2026-05-30: 0 commits2026-05-31: 0 commits2026-06-01: 24 commits2026-06-02: 3 commits2026-06-03: 0 commits2026-06-04: 0 commits2026-06-05: 0 commits2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 2 commits2026-06-09: 16 commits2026-06-10: 0 commits2026-06-11: 0 commits2026-06-12: 0 commits2026-06-13: 0 commits2026-06-14: 6 commits2026-06-15: 1 commit2026-06-16: 0 commits2026-06-17: 0 commits2026-06-18: 0 commits2026-06-19: 1 commit2026-06-20: 0 commits2026-06-21: 0 commits2026-06-22: 0 commits2026-06-23: 6 commits2026-06-24: 0 commits2026-06-25: 1 commit2026-06-26: 0 commits2026-06-27: 0 commits2026-06-28: 0 commits2026-06-29: 0 commits2026-06-30: 0 commits2026-07-01: 0 commits2026-07-02: 91 commits2026-07-03: 9 commits2026-07-04: 0 commits2026-07-05: 2 commits2026-07-06: 0 commits2026-07-07: 0 commits2026-07-08: 0 commits2026-07-09: 17 commits2026-07-10: 0 commits2026-07-11: 0 commits2026-07-12: 0 commits2026-07-13: 0 commits2026-07-14: 0 commits2026-07-15: 0 commits2026-07-16: 0 commits2026-07-17: 0 commits2026-07-18: 0 commits2026-07-19: 0 commits2026-07-20: 2 commits2026-07-21: 0 commits2026-07-22: 4 commits2026-07-23: 0 commits2026-07-24: 0 commits2026-07-25: 0 commits2026-07-26: 0 commits2026-07-27: 0 commits2026-07-28: 0 commits2026-07-29: 0 commits2026-07-30: 0 commits2026-07-31: 0 commits2026-08-01: 0 commits2026-08-02: 4 commits2026-08-03: 0 commits2026-08-04: 0 commits2026-08-05: 4 commits2026-08-06: 0 commits2026-08-07: 0 commits2026-08-08: 0 commits2026-08-09: 0 commits2026-08-10: 0 commits2026-08-11: 0 commits2026-08-12: 0 commits2026-08-13: 0 commits2026-08-14: 0 commits2026-08-15: 0 commits2026-08-16: 0 commits2026-08-17: 0 commits2026-08-18: 0 commits2026-08-19: 0 commits2026-08-20: 0 commits2026-08-21: 0 commits2026-08-22: 0 commits2026-08-23: 0 commits2026-08-24: 0 commits2026-08-25: 0 commits2026-08-26: 0 commits2026-08-27: 0 commits2026-08-28: 0 commits2026-08-29: 0 commits2026-08-30: 0 commits2026-08-31: 0 commits2026-09-01: 0 commits2026-09-02: 0 commits2026-09-03: 0 commits2026-09-04: 0 commits2026-09-05: 0 commits2026-09-06: 0 commits2026-09-07: 32 commits2026-09-08: 0 commits2026-09-09: 0 commits2026-09-10: 0 commits2026-09-11: 0 commits2026-09-12: 0 commits2026-09-13: 0 commits2026-09-14: 0 commits2026-09-15: 0 commits2026-09-16: 0 commits2026-09-17: 0 commits2026-09-18: 0 commits2026-09-19: 6 commits2026-09-20: 0 commits2026-09-21: 0 commits2026-09-22: 0 commits2026-09-23: 0 commits2026-09-24: 9 commits2026-09-25: 0 commits2026-09-26: 0 commits2026-09-27: 0 commits2026-09-28: 0 commits2026-09-29: 0 commits2026-09-30: 0 commits2026-10-01: 0 commits2026-10-02: 0 commits2026-10-03: 0 commits
620 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Very active

    620 commits in 52 weeks

  • Permissive license

    MIT

  • Continuous integration

    Automated checks passing

What apfel does

Apfel is a Swift-based utility that unlocks the Apple FoundationModels LLM natively built into Apple Silicon Macs, making it accessible from the command line. It operates entirely on-device without requiring API keys, cloud connectivity, or massive model downloads. The tool functions in three distinct modes: as a scriptable UNIX CLI utility for quick answers, file summarization, and code generation; as a drop-in local OpenAI-compatible HTTP server (`localhost:11434/v1`) for use with standard SDKs; and as an interactive terminal chat REPL. It leverages Apple Intelligence to provide fast, privacy-preserving AI inference directly on local hardware.

Apfel is built for Mac power users, software developers, and sysadmins running macOS 26 or later on Apple Silicon (M1+). It is perfect for those who want command-line AI capabilities without paying for API usage or compromising local data privacy.

  • Native Apple Intelligence integration: Hooks directly into the Apple FoundationModels API built into macOS 26+ on Apple Silicon.
  • UNIX CLI interface: Provides pipe-friendly commands for text processing, file attachments, structured JSON output, and token counting.
  • OpenAI-compatible server: Exposes a local HTTP server that acts as a drop-in replacement for OpenAI endpoints in existing applications.
  • Zero-download on-device inference: Runs completely offline using the OS's built-in models, requiring no API keys or multi-gigabyte downloads.
  • Multi-format file support: Can parse and summarize text files, extract text from PDFs, and perform OCR on images directly through the CLI.

Where teams use it

Local development assistance

Software developers can pipe `git diff` outputs into Apfel from the terminal to generate automated code reviews or commit messages locally.

Privacy-first scripting

Sysadmins can integrate the CLI tool into shell scripts for text summarization and JSON extraction without sending sensitive data to the cloud.

Offline AI server

AI engineers can test applications built on OpenAI SDKs while on an airplane or offline by pointing them to Apfel's local server.

Automated document OCR

Users can pass images or PDFs to the CLI to rapidly extract text and summarize contents entirely on their device.

Getting started: brew install apfel

README

main branch

apfel

The free AI already on your Mac.

Version 1.12.0 Swift 6.3+ macOS 26 Tahoe+ No Xcode Required License: MIT 100% On-Device Website #agentswelcome

Apple Silicon Macs ship a built-in LLM via Apple FoundationModels. apfel exposes it as a UNIX tool and a local OpenAI-compatible server. 100% on-device. No API keys, no cloud.

Mode Command What you get
UNIX tool apfel "prompt" / echo "text" | apfel Pipe-friendly answers, file attachments, JSON output, exit codes
OpenAI-compatible server apfel --serve Drop-in local http://localhost:11434/v1 backend for OpenAI SDKs

apfel --chat - interactive REPL.

Tool calling works in all contexts. On-device context window: 4096 tokens on macOS 26, 8192 on macOS 27 - read at runtime, see Limitations.

apfel CLI

Requirements & Install

macOS 26 Tahoe+, Apple Silicon (M1+), Apple Intelligence enabled.

brew install apfel

Update:

brew upgrade apfel

Build from source (Command Line Tools with macOS 26.4 SDK / Swift 6.3, no Xcode):

git clone https://github.com/Arthur-Ficial/apfel.git && cd apfel && make install

Nix, same-day tap, Mint, mise, troubleshooting: docs/install.md.

Quick Start

UNIX tool

Quote prompts with ! in single quotes (zsh/bash history expansion): apfel 'Hello, Mac!'.

# Single prompt
apfel "What is the capital of Austria?"

# Permissive mode - reduces guardrail false positives for creative/long prompts
apfel --permissive "Write a dramatic opening for a thriller novel"

# Stream output
apfel --stream "Write a haiku about code"

# Pipe input
echo "Summarize: $(cat README.md)" | apfel

# Attach file content to prompt
apfel -f README.md "Summarize this project"

# Attach multiple files
apfel -f old.swift -f new.swift "What changed between these two files?"

# Attach a PDF or image - on-device text extraction, OCR, and "what the image is about"
apfel -f report.pdf "Summarize the key findings"
apfel -f receipt.jpg "What is the total?"

# Pipe a file straight in (PDF, image, or text)
cat report.pdf | apfel "Summarize this"

# Combine files with piped input
git diff HEAD~1 | apfel -f CONVENTIONS.md "Review this diff against our conventions"

# Only the code - no prose, no markdown fences (pipe-safe, exit 7 if empty)
apfel --code "a python function that deduplicates a list" > dedupe.py
apfel --code "shell one-liner to find the 10 largest files here" | pbcopy

# JSON output for scripting
apfel -o json "Translate to German: hello" | jq .content

# Guaranteed schema-valid JSON output (guided generation)
apfel --schema person.schema.json "Extract the person: Alice is 30." | jq .name

# One-shot multi-turn: conversation JSON in, next assistant turn out
jq '. += [{"role":"user","content":"and in German?"}]' conv.json | apfel --messages -

# Preflight token budget before a large prompt
apfel --count-tokens -f README.md "Summarize this"

# System prompt
apfel -s "You are a pirate" "What is recursion?"

# System prompt from file
apfel --system-file persona.txt "Explain TCP/IP"

# Quiet mode for shell scripts
result=$(apfel -q "Capital of France? One word.")

OpenAI-compatible server

apfel --serve                              # foreground
brew services start apfel                  # background (like Ollama)
brew services stop apfel
APFEL_TOKEN=$(uuidgen) APFEL_MCP=/path/to/tools.py brew services start apfel
curl http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"apple-foundationmodel","messages":[{"role":"user","content":"Hello"}]}'
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused")
resp = client.chat.completions.create(
    model="apple-foundationmodel",
    messages=[{"role": "user", "content": "What is 1+1?"}],
)
print(resp.choices[0].message.content)

Background service details: docs/background-service.md.

Quick testing chat

apfel --chat is a small REPL for testing prompts or MCP servers. For a GUI chat app, see apfel-chat.

apfel --chat
apfel --chat -s "You are a helpful coding assistant"
apfel --chat --mcp ./mcp/calculator/server.py      # chat with MCP tools
apfel --chat --debug                                # debug output to stderr

Ctrl-C exits. Context is trimmed automatically (docs/context-strategies.md).

Demos

Real shell scripts that wrap apfel: cmd (English to shell command), oneliner, mac-narrator, wtd, explain, naming, port, gitsum. They are bundled in the binary - no repo clone. Write them out wherever you installed apfel from (homebrew-core, the tap, or source):

apfel demos ./apfel-demos

That writes every demo (executable) plus a README.md into ./apfel-demos. Then run them from there:

./apfel-demos/cmd "find all .log files modified today"
# $ find . -name "*.log" -type f -mtime -1

./apfel-demos/cmd -x "show disk usage sorted by size"   # -x = execute after confirm
./apfel-demos/cmd -c "list open ports"                   # -c = copy to clipboard

Re-run apfel demos after brew upgrade apfel to refresh. Sources: demo/. Walkthroughs and a copy-paste cmd shell function for your .zshrc: docs/demos.md.

MCP Tool Support

Attach Model Context Protocol servers with --mcp. apfel discovers, invokes, and returns.

apfel --mcp ./mcp/calculator/server.py "What is 15 times 27?"
mcp: ./mcp/calculator/server.py - add, subtract, multiply, divide, sqrt, power, round_number    ← stderr
tool: multiply({"a": 15, "b": 27}) = 405                                          ← stderr
15 times 27 is 405.                                                                ← stdout

Use -q to suppress tool info.

apfel --mcp ./server_a.py --mcp ./server_b.py "Use both tools"
apfel --serve --mcp ./mcp/calculator/server.py
apfel --chat --mcp ./mcp/calculator/server.py

Ships with a calculator at mcp/calculator/ (docs/mcp-calculator.md).

Remote MCP servers (Streamable HTTP, MCP spec 2025-03-26):

apfel --mcp https://mcp.example.com/v1 "what tools do you have?"

# bearer token - prefer env var (flag is visible in ps aux)
APFEL_MCP_TOKEN=mytoken apfel --mcp https://mcp.example.com/v1 "..."

# mixed local + remote
apfel --mcp /path/to/local.py --mcp https://remote.example.com/v1 "..."

Security: prefer APFEL_MCP_TOKEN over --mcp-token (ps aux). apfel refuses bearer tokens over plaintext http://.

apfel-run: optional config layer

apfel itself has no config file - flags + env vars, like any UNIX tool. If you want a TOML config (many MCPs, profiles, team configs in git), apfel-run is an MIT wrapper that adds one via execve drop-in.

brew install Arthur-Ficial/tap/apfel-run
apfel-run config init                 # starter ~/.config/apfel/config.toml
alias apfel=apfel-run                 # optional, every apfel flag still works

OpenAI API Compatibility

Base URL: http://localhost:11434/v1

Feature Status Notes
POST /v1/chat/completions Supported Streaming + non-streaming
POST /v1/responses Supported OpenAI Responses API: string/message input, instructions, streaming (canonical event sequence), text.format (incl. json_schema), function tools (non-streaming). Stateful features (previous_response_id, store: true, background, reasoning, hosted tools) return honest 501s
GET /v1/models Supported Returns apple-foundationmodel
GET /health Supported Model availability, context window, languages
GET /v1/logs, /v1/logs/stats Debug only Requires --debug
Tool calling Supported Native ToolDefinition + JSON detection. See docs/tool-calling-guide.md
response_format: json_object Supported System-prompt injection; markdown fences stripped from output
response_format: json_schema Supported Guaranteed schema-conforming output via FoundationModels DynamicGenerationSchema; works with stream: true
temperature, top_p, max_tokens, seed Supported Mapped to GenerationOptions. top_p is nucleus sampling; temperature: 0 maps to greedy (deterministic). Omitting max_tokens uses the remaining context window (drop-in OpenAI semantics) - see Default response cap
stream: true Supported SSE; final usage chunk only when stream_options: {"include_usage": true} (per OpenAI spec)
finish_reason Supported stop, tool_calls, length
Context strategies Supported x_context_strategy, x_context_max_turns, x_context_output_reserve extension fields
CORS Supported Enable with --cors
POST /v1/completions 501 Legacy text completions not supported
POST /v1/embeddings 501 Embeddings not available on-device
logprobs=true, n>1, stop, presence_penalty, frequency_penalty 400 Rejected explicitly. n=1 and logprobs=false are accepted as no-ops
Multi-modal (images) 400 Rejected with clear error
Authorization header Supported Required when --token is set. See docs/server-security.md

Full API spec: openai/openai-openapi.

Default response cap (max_tokens)

When max_tokens is omitted, CLI and OpenAI-compatible server behave identically: the value flows through as nil and the model uses whatever room is left in the context window. This is drop-in OpenAI semantics - no arbitrary fallback constant.

The on-device context window holds input and output combined: 4096 tokens on macOS 26, 8192 on macOS 27. apfel reads the real size at runtime via SystemLanguageModel.contextSize - check yours with apfel --model-info. If generation runs into the ceiling, the response ends cleanly with finish_reason: "length" and the partial content is returned (server: HTTP 200; CLI: exit 0 with a stderr warning). Pass max_tokens explicitly when you want a tighter latency budget or a known cap for your client.

Examples

# Omitted: uses remaining window, finish_reason: "stop" or "length"
curl -sS http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"apple-foundationmodel",
       "messages":[{"role":"user","content":"Reply SKIP, MOVE, or RENAME."}]}'

# Explicit cap (recommended for tight latency budgets)
curl -sS http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"apple-foundationmodel","max_tokens":128,
       "messages":[{"role":"user","content":"Summarise: ..."}]}'

Picking a value

Use case max_tokens
Single-word / classification reply 16 - 32
One-line instruction 64 - 128
Short paragraph 256 - 512
Long paragraph / structured JSON 1024 - 2048
As long as the context window allows omit it

Keep input_tokens + max_tokens comfortably below the context window (4096 tokens on macOS 26, 8192 on macOS 27). If the prompt itself exceeds the window, generation cannot start and the request fails with [context overflow] (HTTP 400 / CLI exit 4). The validator rejects non-positive values (max_tokens <= 0).

CLI parity

CLI and server share one rule: omitted = use remaining window. No constant to drift. Override with --max-tokens N or APFEL_MAX_TOKENS=N.

apfel "Reply SKIP."                    # uses remaining window
apfel --max-tokens 64 "Reply SKIP."    # explicit cap
APFEL_MAX_TOKENS=2048 apfel "..."      # via env var

Permissive guardrails for the server

apfel --serve --permissive makes the server use Apple's .permissiveContentTransformations guardrails for every request the process handles. Same flag, same semantics as the CLI's --permissive (docs/PERMISSIVE.md). There is no per-request override - the server operator decides for the whole process.

apfel --serve --permissive             # every request uses permissive guardrails

Limitations

Constraint Detail
Context window 4096 tokens on macOS 26, 8192 on macOS 27 (input + output combined). Not a hardcoded constant - apfel reads SystemLanguageModel.contextSize at runtime; apfel --model-info prints the live value
Platform macOS 26+, Apple Silicon only
Model One model (apple-foundationmodel, ~3B params on-device), not configurable
Guardrails Apple's safety system may block benign prompts. --permissive reduces false positives (docs/PERMISSIVE.md)
Speed On-device, not cloud-scale - a few seconds per response
No embeddings / vision Not available on-device
Training data / knowledge cutoff Apple has not published a precise cutoff for the on-device model. When pushed to name one, the model confabulates a different date each sample (e.g. "October 2023", "April 2023"). Treat all model self-reports about its own training as unreliable.
No current date or real-time awareness The model does not know today's date and has no network/clock access. If asked, it will either refuse or invent a date. Inject the current date via system prompt when arithmetic depends on it (see workaround below).

Workaround for date-dependent prompts - inject the current date as a system message:

apfel -s "Today is $(date '+%B %d, %Y')." "Write a one-line release note dated today."
apfel --chat -s "Today is $(date '+%B %d, %Y'). You are a helpful assistant."

Note: even with an injected date the 3B model can still hallucinate (especially when asked directly about its own training cutoff). The injection helps generative prompts that use the date; it does not override the model's self-report reflex.

Background: #158.

Reference Docs

Guides to use apfel from Python, Node.js, Ruby, PHP, Bash/curl, Zsh, AppleScript, Swift, Perl, AWK - see docs/guides/index.md. Empirically tested; runnable proof at apfel-guides-lab.

Architecture

CLI (single/stream/chat) ──┐
                           ├─→ FoundationModels.SystemLanguageModel
HTTP Server (/v1/*) ───────┘   (100% on-device, zero network)
                                ContextManager → Transcript API
                                SchemaConverter → native ToolDefinitions
                                TokenCounter → real token counts (SDK 26.4)

Swift 6.3 strict concurrency. Three targets: ApfelCore (pure logic, unit-testable, also available as a Swift Package product - linked under Reference Docs above), apfel (CLI + server), and apfel-tests (pure Swift runner, no XCTest).

Build & Test

make test                                # release build + all unit/integration tests
make preflight                           # full release qualification
make install                             # build release + install to /usr/local/bin
make build                               # build release only
make version                             # print current version
make release                             # patch release
make release TYPE=minor                  # minor release
make release TYPE=major                  # major release
swift build                              # quick debug build (no version bump)
swift run apfel-tests                    # unit tests
python3 -m pytest Tests/integration/ -v  # integration tests
apfel --benchmark -o json                # performance report

.version is the single source of truth. Only make release bumps versions. Local builds do not change the version.

The apfel tree

Projects built on apfel. Each ships as its own repo + Homebrew formula.

Project What it does Install
apfel The root. On-device FoundationModels CLI + OpenAI-compatible server. brew install apfel
apfel-chat macOS chat client: streaming markdown, speech I/O, Apple Vision image analysis. brew install Arthur-Ficial/tap/apfel-chat
apfel-clip Menu-bar AI actions on the clipboard: summarize, translate, rewrite. brew install Arthur-Ficial/tap/apfel-clip
apfel-quick Instant AI overlay: press a key, ask, answer, dismiss. brew install Arthur-Ficial/tap/apfel-quick
apfelpad Formula notepad - on-device AI as an inline cell function. brew install Arthur-Ficial/tap/apfelpad
apfel-mcp Token-budget-optimized MCPs for the small on-device window: url-fetch, ddg-search, search-and-fetch. brew install Arthur-Ficial/tap/apfel-mcp
apfel-gui SwiftUI debug inspector: request timeline, MCP protocol viewer, TTS/STT. brew install Arthur-Ficial/tap/apfel-gui
apfel-run UNIX wrapper adding a persistent MCP registry + TOML config on top of apfel. brew install Arthur-Ficial/tap/apfel-run
apfel-tag On-device content tagging CLI: pipe text in, get tags/topics/emotions out. brew install Arthur-Ficial/tap/apfel-tag
apfel-server-kit Swift package for ecosystem tools: discover, spawn, and stream from a local apfel --serve. Swift Package

Community Projects

Built something on top of apfel? Open an issue and it can be added here.

Project What it does Links
apfelclaw by @julianYaman Local AI agent that reads files, calendar, mail, and Mac status via read-only tools github - site
fruit-chat by @bhaskarvilles Browser-based chat UI that talks to apfel --serve over the OpenAI-compatible API github
local-claude by @lucaspwo Claude Code wrapper that swaps in apfel as a local backend via a small Anthropic-OpenAI proxy github
apfeller by @hasit App manager for local shell apps built around apfel github - site - catalog
apfel-for-raycast by @eggsy Raycast command bar extension: ask, translate, explain files and directories, conversation history, custom system prompts. On-device via apfel CLI. store - github

Contributing

Issues and PRs welcome on any Arthur-Ficial/apfel* repo.

#agentswelcome - AI agent PRs are fine. Read the repo's CLAUDE.md, run the tests, credit the tool in a Co-Authored-By trailer. Same bar as humans: clean code, passing tests, honest limits. Most agent-friendly entry point: apfel-mcp (contribution rules).

License

MIT

View on GitHub

Recent activity

commits and pull requests

Recent open issues

view all

Releases and announcements

85 total
  1. v1.12.0v1.12.0Sep 24, 2026644 downloads

    ## What's Changed - 9c7d6e5 feat(schema): resolve local $ref/$defs, enforce numeric and array bounds, reject unrepresentable constraints (#479) (#501) - 50677af docs(claude): sync version 1.11.1 and test counts (1208 unit, 533 integration) --- Install: `brew install apfel` Upgrade: `brew upgrade apfel`

  2. v1.11.1v1.11.1Sep 24, 202656 downloads

    ## What's Changed - 5a5d1ad fix(mcp): price the auto-execute follow-up as it is built so #221 truncation leaves room for the pinned exchange (#482) - a1edaf7 fix(context): keep tool-call exchanges whole when trimming client-supplied history (#482) (#500) - 568f7f1 fix(server): enforce tool_choice and parallel_tool_calls at the response boundary (#480) (#489) - e5f4f0b fix(build): pin --build-system native for CLT-only environments (#194) (#499) --- Install: `brew install apfel` Upgrade: `brew upgrade apfel`

  3. v1.11.0v1.11.0Sep 19, 2026624 downloads

    ## What's Changed - 791ca79 fix(cli): drop exported APFEL_* prompt defaults in input-ignoring modes instead of exiting 2 (#496) (#498) - 4ea345e fix(context): stop reporting the assumed window floor as a measurement (#491) (#494) - 48408e1 docs(integrations): make the copy-paste token limits macOS-27-correct (#495) - a98c391 docs(coreai-impact): drop stale 'all Beta' after macOS 27 GA (#196) (#490) - 1a7a5ba fix(server): honor max_completion_tokens from modern OpenAI clients (#478) (#488) - f8942de docs(claude): sync to v1.10.0 and the new test counts --- Install: `brew install apfel` Upgrade: `brew upgrade apfel`

  4. v1.10.0v1.10.0Sep 7, 2026867 downloads

    ## What's Changed - 5f5114b fix(tests): assert the truncation marker where it actually lands (#464) - c22939c fix(tests): correct two model-test defects that blocked the release gate - ae09e9d fix(mcp): bound stdin writes with poll and a deadline (#418) - 6feb43f refactor(server): unify chatFailure and responsesFailure into openAIFailure (#437) - 7c32c61 fix(security): create the chat-history file 0600 before writing prompts into it (#473) - f45d04f fix(stream): detect a divergent retry instead of splicing two generations (#402) - d0984ad fix(session): fail loudly when the MCP re-prompt cap is exhausted (#435) - 68a62f2 fix(cli): drain the pipe before waiting for exit in shellOutput (#433) - 0a0425e fix(mcp): propagate a failed remote initialized notification (#432) - 4f8fea9 fix(mcp): move blocking stdio I/O off the cooperative pool (#431) - 2e06983 fix(responses): emit response.incomplete for truncated streams (#412) - 530af98 fix(responses): 400 for malformed content, reject image parts (#409) - 812c3ab fix(validation): reject unknown message roles instead of silently dropping them (#405) - 6496655 fix(count-tokens): include the prompt in the total when --mcp is used (#399) - 2

  5. v1.9.1v1.9.1Aug 5, 20261.1K downloads

    ## What's Changed - 0e92fc6 docs(changelog): note hummingbird 2.26.0 bump for next release (#382) - f6d41f4 build(deps): Bump github.com/hummingbird-project/hummingbird (#382) - 334dc2c docs(claude): sync to v1.9.0 and unit count (1042) --- Install: `brew install apfel` Upgrade: `brew upgrade apfel`

Code frequency

additions and deletions
+44.5K-44.5KWeek of 2026-03-22: +23,069 linesWeek of 2026-03-22: -9,483 linesWeek of 2026-03-29: +12,602 linesWeek of 2026-03-29: -9,124 linesWeek of 2026-04-05: +11,458 linesWeek of 2026-04-05: -3,317 linesWeek of 2026-04-12: +44,529 linesWeek of 2026-04-12: -5,294 linesWeek of 2026-04-19: +4,054 linesWeek of 2026-04-19: -696 linesWeek of 2026-04-26: +894 linesWeek of 2026-04-26: -801 linesWeek of 2026-05-03: +562 linesWeek of 2026-05-03: -17 linesWeek of 2026-05-10: +129 linesWeek of 2026-05-10: -92 linesWeek of 2026-05-17: +719 linesWeek of 2026-05-17: -147 linesWeek of 2026-05-24: +62 linesWeek of 2026-05-24: -14 linesWeek of 2026-05-31: +1,959 linesWeek of 2026-05-31: -516 linesWeek of 2026-06-07: +749 linesWeek of 2026-06-07: -96 linesWeek of 2026-06-14: +1,460 linesWeek of 2026-06-14: -307 linesWeek of 2026-06-21: +803 linesWeek of 2026-06-21: -28 linesWeek of 2026-06-28: +13,541 linesWeek of 2026-06-28: -3,456 linesWeek of 2026-07-05: +2,515 linesWeek of 2026-07-05: -698 linesWeek of 2026-07-12: +0 linesWeek of 2026-07-12: -0 linesWeek of 2026-07-19: +243 linesWeek of 2026-07-19: -79 linesWeek of 2026-07-26: +0 linesWeek of 2026-07-26: -0 linesWeek of 2026-08-02: +115 linesWeek of 2026-08-02: -73 linesWeek of 2026-08-09: +0 linesWeek of 2026-08-09: -0 linesMar 22, 2026Aug 9, 2026
+119.5K lines added, -34.2K removed over the last year.

Commits per week

last 52 weeks
1090Week of 2025-10-05: 0 commitsWeek of 2025-10-12: 0 commitsWeek of 2025-10-19: 0 commitsWeek of 2025-10-26: 0 commitsWeek of 2025-11-02: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 0 commitsWeek of 2026-02-08: 0 commitsWeek of 2026-02-15: 0 commitsWeek of 2026-02-22: 0 commitsWeek of 2026-03-01: 0 commitsWeek of 2026-03-08: 0 commitsWeek of 2026-03-15: 0 commitsWeek of 2026-03-22: 56 commitsWeek of 2026-03-29: 80 commitsWeek of 2026-04-05: 109 commitsWeek of 2026-04-12: 55 commitsWeek of 2026-04-19: 38 commitsWeek of 2026-04-26: 14 commitsWeek of 2026-05-03: 3 commitsWeek of 2026-05-10: 9 commitsWeek of 2026-05-17: 15 commitsWeek of 2026-05-24: 1 commitsWeek of 2026-05-31: 27 commitsWeek of 2026-06-07: 18 commitsWeek of 2026-06-14: 8 commitsWeek of 2026-06-21: 7 commitsWeek of 2026-06-28: 100 commitsWeek of 2026-07-05: 19 commitsWeek of 2026-07-12: 0 commitsWeek of 2026-07-19: 6 commitsWeek of 2026-07-26: 0 commitsWeek of 2026-08-02: 8 commitsWeek of 2026-08-09: 0 commitsWeek of 2026-08-16: 0 commitsWeek of 2026-08-23: 0 commitsWeek of 2026-08-30: 0 commitsWeek of 2026-09-06: 32 commitsWeek of 2026-09-13: 6 commitsWeek of 2026-09-20: 9 commitsWeek of 2026-09-27: 0 commitsOct 5, 2025Sep 27, 2026
620 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 0 commitsSun 1:00 — 5 commitsSun 2:00 — 2 commitsSun 3:00 — 0 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 0 commitsSun 7:00 — 0 commitsSun 8:00 — 5 commitsSun 9:00 — 5 commitsSun 10:00 — 5 commitsSun 11:00 — 14 commitsSun 12:00 — 1 commitsSun 13:00 — 2 commitsSun 14:00 — 0 commitsSun 15:00 — 0 commitsSun 16:00 — 0 commitsSun 17:00 — 0 commitsSun 18:00 — 0 commitsSun 19:00 — 0 commitsSun 20:00 — 2 commitsSun 21:00 — 9 commitsSun 22:00 — 4 commitsSun 23:00 — 12 commitsMon 0:00 — 6 commitsMon 1:00 — 0 commitsMon 2:00 — 0 commitsMon 3:00 — 0 commitsMon 4:00 — 0 commitsMon 5:00 — 0 commitsMon 6:00 — 0 commitsMon 7:00 — 0 commitsMon 8:00 — 0 commitsMon 9:00 — 3 commitsMon 10:00 — 1 commitsMon 11:00 — 2 commitsMon 12:00 — 8 commitsMon 13:00 — 10 commitsMon 14:00 — 12 commitsMon 15:00 — 14 commitsMon 16:00 — 23 commitsMon 17:00 — 3 commitsMon 18:00 — 2 commitsMon 19:00 — 0 commitsMon 20:00 — 0 commitsMon 21:00 — 0 commitsMon 22:00 — 1 commitsMon 23:00 — 0 commitsTue 0:00 — 6 commitsTue 1:00 — 2 commitsTue 2:00 — 0 commitsTue 3:00 — 0 commitsTue 4:00 — 1 commitsTue 5:00 — 0 commitsTue 6:00 — 0 commitsTue 7:00 — 0 commitsTue 8:00 — 0 commitsTue 9:00 — 5 commitsTue 10:00 — 6 commitsTue 11:00 — 12 commitsTue 12:00 — 7 commitsTue 13:00 — 6 commitsTue 14:00 — 15 commitsTue 15:00 — 0 commitsTue 16:00 — 1 commitsTue 17:00 — 0 commitsTue 18:00 — 2 commitsTue 19:00 — 0 commitsTue 20:00 — 5 commitsTue 21:00 — 1 commitsTue 22:00 — 11 commitsTue 23:00 — 6 commitsWed 0:00 — 1 commitsWed 1:00 — 2 commitsWed 2:00 — 0 commitsWed 3:00 — 0 commitsWed 4:00 — 0 commitsWed 5:00 — 0 commitsWed 6:00 — 0 commitsWed 7:00 — 0 commitsWed 8:00 — 1 commitsWed 9:00 — 6 commitsWed 10:00 — 10 commitsWed 11:00 — 13 commitsWed 12:00 — 11 commitsWed 13:00 — 6 commitsWed 14:00 — 1 commitsWed 15:00 — 15 commitsWed 16:00 — 22 commitsWed 17:00 — 1 commitsWed 18:00 — 0 commitsWed 19:00 — 0 commitsWed 20:00 — 6 commitsWed 21:00 — 1 commitsWed 22:00 — 3 commitsWed 23:00 — 2 commitsThu 0:00 — 9 commitsThu 1:00 — 18 commitsThu 2:00 — 14 commitsThu 3:00 — 13 commitsThu 4:00 — 8 commitsThu 5:00 — 11 commitsThu 6:00 — 8 commitsThu 7:00 — 0 commitsThu 8:00 — 0 commitsThu 9:00 — 6 commitsThu 10:00 — 2 commitsThu 11:00 — 5 commitsThu 12:00 — 6 commitsThu 13:00 — 12 commitsThu 14:00 — 10 commitsThu 15:00 — 24 commitsThu 16:00 — 7 commitsThu 17:00 — 8 commitsThu 18:00 — 6 commitsThu 19:00 — 0 commitsThu 20:00 — 0 commitsThu 21:00 — 9 commitsThu 22:00 — 0 commitsThu 23:00 — 0 commitsFri 0:00 — 3 commitsFri 1:00 — 4 commitsFri 2:00 — 6 commitsFri 3:00 — 0 commitsFri 4:00 — 0 commitsFri 5:00 — 0 commitsFri 6:00 — 0 commitsFri 7:00 — 0 commitsFri 8:00 — 1 commitsFri 9:00 — 0 commitsFri 10:00 — 5 commitsFri 11:00 — 7 commitsFri 12:00 — 3 commitsFri 13:00 — 3 commitsFri 14:00 — 3 commitsFri 15:00 — 6 commitsFri 16:00 — 6 commitsFri 17:00 — 0 commitsFri 18:00 — 3 commitsFri 19:00 — 9 commitsFri 20:00 — 3 commitsFri 21:00 — 1 commitsFri 22:00 — 2 commitsFri 23:00 — 3 commitsSat 0:00 — 1 commitsSat 1:00 — 3 commitsSat 2:00 — 1 commitsSat 3:00 — 0 commitsSat 4:00 — 0 commitsSat 5:00 — 0 commitsSat 6:00 — 0 commitsSat 7:00 — 1 commitsSat 8:00 — 0 commitsSat 9:00 — 4 commitsSat 10:00 — 5 commitsSat 11:00 — 0 commitsSat 12:00 — 0 commitsSat 13:00 — 7 commitsSat 14:00 — 5 commitsSat 15:00 — 4 commitsSat 16:00 — 1 commitsSat 17:00 — 1 commitsSat 18:00 — 0 commitsSat 19:00 — 1 commitsSat 20:00 — 0 commitsSat 21:00 — 1 commitsSat 22:00 — 1 commitsSat 23:00 — 2 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.

Who is committing

last 52 weeks
Maintainer commits589 (93%)
Community commits41 (7%)

630 commits in total over the last year.

DateListRankStars gained
Apr 5, 2026daily#23+229
  • affaan-m/ECC

    The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

    272.8K stars · JavaScript

  • NousResearch/hermes-agent

    The agent that grows with you

    251.2K stars · Python

  • n8n-io/n8n

    Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

    206.7K stars · TypeScript

  • yt-dlp/yt-dlp

    A feature-rich command-line audio/video downloader

    195.5K stars · Python

  • firecrawl/firecrawl

    Supercharge your AI agents with data from the web and beyond. Building the library for superintelligence. 🔥

    188.6K stars · TypeScript

  • Significant-Gravitas/AutoGPT

    AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.

    187.7K stars · Python