Write an app
This page focuses on writing and testing an apfeller app locally. It stays intentionally focused on authoring and local validation rather than the public release workflow.
apfeller app sources live in the separate apfeller-apps repo. Each app lives under apps/<id>/app.toml, with optional shell hooks under apps/<id>/hooks/.
App Layout
apps/<id>/app.toml: the app definitionapps/<id>/hooks/build_prompt.sh: optional hook that prints the final promptapps/<id>/hooks/pre_run.sh: optional hook that validates or prepares before the app runs
Minimal Example
id = "folder-brief"
summary = "Turn a short topic into a two-sentence brief."
description = "Generate a short shell-friendly explanation for a topic or path."
command = "folder-brief"
kind = "ai-text"
requires_commands = ["apfel"]
supported_shells = ["fish", "zsh"]
[help]
usage = 'folder-brief "what to summarize"'
examples = ['folder-brief "Downloads"', 'folder-brief "launchd agents"']
[input]
mode = "rest"
name = "topic"
required = true
[prompt]
system = "You write a concise two-sentence brief for a macOS terminal user. No bullets. No markdown."
template = "Topic: "
max_context_tokens = 4096
max_input_bytes = 512
max_output_tokens = 80
[output]
mode = "text"
Required Fields and Sections
Required top-level fields:
idsummarydescriptioncommandkindrequires_commandssupported_shells
Required sections for the currently supported app kinds:
[help]withusageandexamples[input]withmode,name, andrequired[output]withmode[prompt]withsystem,template,max_context_tokens,max_input_bytes, andmax_output_tokens
Supported kind values:
ai-commandai-text
Valid kind and output.mode pairs:
ai-command+shell_commandai-text+textai-text+structured_text
Optional Parts
Optional [[args]] blocks let you add flags and options. Supported arg types:
flagstringintegerenum
Each [[args]] block uses:
nametypelongshortoptionaldescriptiondefaultoptionalchoicesforenum
Optional hooks live under [hooks]:
build_prompt = "hooks/build_prompt.sh"pre_run = "hooks/pre_run.sh"
Use build_prompt when the prompt should be assembled by a shell script rather than a static template. Use pre_run for validation or setup that should stop the app when it exits non-zero.
Local Test Loop
Create your app under apfeller-apps/apps/<id>/, then package a local catalog and test the installed command against it:
sh scripts/package_catalog.sh --output-dir dist --bundle-base-url "file://$PWD/dist"
Point apfeller at that local catalog:
APFELLER_CATALOG_URL="file://$PWD/dist/apfeller-catalog.tsv" apfeller list
Install your app from that same local catalog:
APFELLER_CATALOG_URL="file://$PWD/dist/apfeller-catalog.tsv" apfeller install folder-brief
Run the installed command:
folder-brief "Downloads"
If your app requires extra commands such as apfel, install those first so the manager can run the app successfully.