10. Calling it from other programs
So far a person has read the help and the errors of heat. A workflow manager, a Python script or an AI agent also calls programs, and it has to work out what a program takes, whether a command line is valid, and what to do when a call fails. Text meant for a person makes it guess. This chapter gives it answers it can parse, with the same definitions.
Everything here is opt-in, and a program using none of it behaves as before.
call cli%init(progname='heat', version='v1.0', description='Solve the 2D heat equation on a square plate', &
schema_option=.true., check_option=.true., dry_run_option=.true., error_format_env='HEAT_ERROR_FORMAT')call cli%add(switch='--write-skill', help='Write the agent skill and stop', required=.false., def='.false.', act='store_true')
call cli%add_group(group='run', description='Run a simulation')
call cli%add(group='run', switch='--steps', help='Time steps', required=.true., val_type='I4P', min='1')
call cli%add(group='run', switch='--cfl', help='CFL number', required=.false., def='0.25', val_type='R8P', min='0', max='1')
call cli%add(group='run', switch='--label', help='Label of the run', required=.false., def='run')
call cli%add(group='run', switch='--token', help='Access token', required=.false., def='', sensitive=.true.)Say what a value is
val_type declares the type of a value, so that parse can check it before the program starts, from any source. Without it FLAP only knows a value when get reads it, possibly hours into a run. --steps and --cfl declare theirs; --label is free text.
Describe the program
schema_option=.true. adds --schema, a JSON description of the program. A caller reads this once and knows every option, type, range and default. heat run --schema describes one command, so a caller need not load the whole program:
$ heat run --schema | head -n 12
{
"schema_version": 1,
"program": {"name": "heat", "version": "v1.0"},
"command":
{"name": "run", "aliases": [], "description": "Run a simulation", "exclusive_with": [],
"options": [
{"switch": "--steps", "help": "Time steps", "required": true, "action": "store", "value": {"type": "integer", "minimum": 1, "maximum": 2147483647, "x-flap-kind": "I4P"}, "typed": true},
{"switch": "--cfl", "help": "CFL number", "required": false, "action": "store", "value": {"type": "number", "minimum": 0, "maximum": 1, "x-flap-kind": "R8P"}, "typed": true, "default": "0.25"},
{"switch": "--label", "help": "Label of the run", "required": false, "action": "store", "value": {"type": "string"}, "typed": false, "default": "run"},
{"switch": "--token", "help": "Access token", "required": false, "action": "store", "value": {"type": "string"}, "typed": false, "sensitive": true}
],
"positionals": [],Check before running
check_option=.true. adds --check, before or after the command. It runs every stage of parse and stops:
$ heat --check run --steps 100 --cfl 0.4 --label demo
heat: command line valid
2 options not type-checked: run --label run --tokenIt names what it could not validate: here --label and --token, which have no declared type. A wrong value is reported with no run at all:
$ heat --check run --steps 100 --cfl 7
heat: error: value "7" of "--cfl" is out of range [0, 1]!
Try 'heat run --help' for help.
[exit status 2]Errors a program can read
error_format_env='HEAT_ERROR_FORMAT' lets the caller ask for JSON errors, one line each, without colours or usage. The line has the code, what to change (class), the option at fault and FLAP's suggestions:
$ HEAT_ERROR_FORMAT=json heat run --stepz 100
{"schema_version":1,"level":"error","code":15,"class":"usage","message":"heat: error: switch \"--stepz\" is unknown! Did you mean \"--steps\"?","argument":"--stepz","suggestions":["--steps"]}
[exit status 2]Exit statuses that say what to do
End the program with cli%exit_status():
call cli%parse(error=error)
if (error /= 0) stop cli%exit_status(), quiet=.true.$ heat run --steps many
heat: error: cannot convert "many" of option "--steps" to an integer!
Try 'heat run --help' for help.
[exit status 2]Status 2 tells the caller to fix the command line and retry; 3, to fix a file or a variable; 4, not to retry at all, because the program itself is wrong. Your own failures keep status 1. The whole table is in Error Codes.
Try it without doing it
dry_run_option=.true. adds --dry-run, but only your program can honour it:
if (cli%is_dry_run()) then
print '(A,I0,A)', 'would run ', steps, ' steps'
stop
endif$ heat --dry-run run --steps 100
would run 100 stepsA promise FLAP cannot keep for you
A program that enables --dry-run and forgets to read it performs the real action while its caller believes it did not. Enable the option only once every side effect is guarded.
Record what ran
save_provenance writes a manifest of the run: the program, the command line, and each value with its source. It has no date, host or directory, so the same inputs give the same bytes. sensitive=.true. keeps --token out of it, as ***, and out of error messages:
call cli%save_provenance('heat.manifest.json', error=error)$ heat run --steps 100 --token s3cret && cat heat.manifest.json
ran 100 steps
{
"schema_version": 1,
"program": {"name": "heat", "version": "v1.0"},
"arguments": ["run","--steps","100","--token","***"],
"status": 0,
"dry_run": false,
"values": [
{"command": "", "name": "--write-skill", "value": ".false.", "source": "default"},
{"command": "run", "name": "--steps", "value": "100", "source": "command line"},
{"command": "run", "name": "--cfl", "value": "0.25", "source": "default"},
{"command": "run", "name": "--label", "value": "run", "source": "default"},
{"command": "run", "name": "--token", "value": "***", "source": "command line"}
]
}Introduce it to an agent
save_skill writes a SKILL.md, the Agent Skills format, from the same definitions, so it cannot fall out of date as long as you regenerate it at build time:
call execute_command_line('mkdir -p heat')
call cli%save_skill('heat/SKILL.md', error=error)$ heat --write-skill && cat heat/SKILL.md
---
name: heat
description: "Solve the 2D heat equation on a square plate. Use when running heat."
---
# heat
## Options
- `--write-skill`: Write the agent skill and stop
## Commands
Read the options of a command with `heat COMMAND --schema`.
- `run`: Run a simulation
## Calling it
- `--schema` prints the full description as JSON.
- `--check` validates a command line without running it, and names what it could not validate.
- `--dry-run` reports what would be done, without doing it.
- `HEAT_ERROR_FORMAT=json` writes errors as JSON lines.
- Exit status: 0 success; 1 failure of the program; 2 fix the command line and retry; 3 fix a file or a variable; 4 the program is wrong, do not retry.A good --help already serves an agent that has been told about the program (the measurement). The skill is how an agent learns that the program exists.
In a script
A caller needs no FLAP code. flap_tool.py, about 70 lines of Python, turns --schema into the tool description an LLM API expects, and checks each call with --check.
What you learned
val_type, --schema, --check, JSON errors, exit_status, --dry-run, save_provenance, sensitive, save_skill. Reference: Machine-readable Interfaces.
That is the whole tour. For quick answers, see the cookbook.