| title | Your first run | |||
|---|---|---|---|---|
| permalink | /tutorials/first-run | |||
| diataxis | tutorial | |||
| redirect_from |
|
This tutorial walks you through writing and running your first Jaiph program. By the end of it you will have written a single .jh file, run it with the jaiph CLI, watched the live progress tree, and looked at the run artifacts the runtime writes under .jaiph/runs/.
This tutorial uses only script steps, so you do not need an agent backend or API keys. The follow-up tutorial Your first agent run adds a prompt step on top of what you build here.
You will write a file with one script step that prints a greeting, and a return that passes the script's output back as the run's return value. The whole file is five lines.
- A POSIX shell (
sh,bash,zsh) withcurl,minisign, and eithershasumorsha256sumavailable. The installer usesminisignto verify the release signature andshasumorsha256sumto verify the checksum. It fails closed ifminisignis missing. See Install and switch versions if you need to install withoutminisign. - About five minutes.
Node and API keys are not required for this tutorial. jaiph run executes on the host.
Install the standalone binary:
curl -fsSL https://jaiph.org/install | bashThe installer downloads a per-platform binary, verifies its signature and checksum, and writes it to ~/.local/bin/jaiph. See Install and switch versions for other options, such as JAIPH_BIN_DIR and version switching.
Confirm the install:
jaiph --versionIf the command is not found, prepend the install directory to PATH:
export PATH="$HOME/.local/bin:$PATH"Create a fresh directory and write a file named hello.jh:
script greet = 'echo "Hello, ${1:-world}!"'
export def main(who) {
return greet(who)
}
Here is what each line does:
script greetdeclares a managed script. The body in the example is a single-line bash command. It uses shell positional arguments such as$1and$2, not Jaiph${name}interpolation.${1:-world}is bash default expansion, which suppliesworldwhengreet(...)passes no value. For a multi-line body or another interpreter, see the Script RHS section of the grammar reference.export def main(who)is the run entry. Every.jhfile invoked withjaiph runenters atexport def main. Thewhoparameter is bound by position from the CLI arguments after the file path.return greet(who)calls the script withwhoas${1}, captures its stdout as the step value, and returns it as the run's return value.
jaiph run ./hello.jh "Adam"Before any step runs, the CLI prepares the file in two steps:
- The CLI loads the entry file and its import closure into a
ModuleGraphonce. This file has no imports, so the closure is one module. - The CLI validates the graph and emits each
scriptbody as an executable file under a temporaryscripts/directory that$JAIPH_SCRIPTSpoints to. Def steps stay as interpreted AST, so there is no transpiledmain.sh.
You should see this (timings will differ):
Jaiph: Running hello.jh
def main (who="Adam")
▸ script greet (1="Adam")
✓ script greet (0s)
✓ PASS def main (0.2s)
Hello, Adam!
The first line is the run banner. The def main row and the indented ▸ and ✓ rows are the live progress tree. A ▸ marks a step that has started, a ✓' marks a step that has finished, and '(0s) is the elapsed time for that step. The root row is static, and only nested steps print ▸ and ✓ lines. The Hello, Adam! line after PASS is the return value of export def main, which jaiph run prints on stdout after a successful run.
Every run writes durable files under .jaiph/runs/<YYYY-MM-DD>/<HH-MM-SS>-<entry>/ in UTC. List the most recent run:
ls -la .jaiph/runs/*/*/The layout you should see:
000001-def__main.outand.errhold the captured stdout and stderr for the entry def.000002-script__greet.outand.errhold the captured stdout and stderr for thegreetscript step.return_value.txtholds the valuedef mainreturned, and it is written only on success.run_summary.jsonlis the durable event timeline, with records such asRUN_START,STEP_START,STEP_END, andRUN_END.heartbeatis a liveness file that holds an epoch-milliseconds timestamp, refreshed about every 10 seconds while the run is active.
Read the captured script output and the return value:
cat .jaiph/runs/*/*/000002-script__greet.out
cat .jaiph/runs/*/*/return_value.txtBoth should match the line printed after PASS. The full artifact layout is documented in the durable artifact layout section of the architecture page. The event types in run_summary.jsonl are documented in the run artifacts section of the CLI reference.
Replace the script body with one that exits non-zero:
script greet = 'echo "Hello, ${1:-world}!" && exit 7'
export def main(who) {
return greet(who)
}
Re-run with the same arguments:
jaiph run ./hello.jh "Adam"The CLI prints a ✗ FAIL line on stderr, then a block with Logs:, Summary:, out:, and err: lines that point to the run directory, followed by an Output of failed step: excerpt. The process exits non-zero. return_value.txt is not written on failure, only on success.
Revert the failing script body so the run passes again, then pick a direction:
- Your first agent run adds a
promptstep that calls an agent backend. - Pass a host key to a script shows how to grant one host secret with
useand--env. - Language reference covers every step type and expression kind, with their allowed positions and capture rules.
- CLI reference covers every
jaiphsubcommand and flag. - Architecture explains how the CLI, parser, validator, transpiler, runtime, and contracts fit together.