| title | CLI | ||
|---|---|---|---|
| permalink | /reference/cli | ||
| diataxis | reference | ||
| redirect_from |
|
This page is the authoritative inventory of the jaiph CLI: every subcommand, every flag, every exit-relevant behaviour. It does not explain how to choose between commands — see Why Jaiph for context and the how-to pages for recipes.
The published jaiph bin is node dist/src/cli.js (npm) or the standalone dist/jaiph (Bun-compiled). Both dispatch through src/cli/index.ts.
| Form | Effect |
|---|---|
jaiph |
Print the overview and exit 0. |
jaiph --help / -h |
Print the overview and exit 0. |
jaiph --version / -v |
Print the CLI version and exit 0. |
jaiph <subcommand> [-h | --help] |
Print the subcommand's usage (flags + one example) and exit 0. Recognised anywhere in the arg list before -- (except compile: help flags must precede path arguments). |
jaiph <path> |
File shorthand. Paths ending in *.test.jh route to jaiph test; other *.jh paths route to jaiph run. Non-existent paths fall through to normal command parsing. |
jaiph <unknown> |
Print Unknown command: <name>, repeat the overview, exit 1. |
The reserved internal marker __workflow-runner is excluded from --help/usage and from the file-shorthand path; it is used by process.execPath self-spawn (see Architecture — Distribution: Node vs Bun standalone).
| Subcommand | Purpose |
|---|---|
run |
Compile, launch, and observe one workflow run (with optional Docker sandboxing). |
test |
Execute *.test.jh blocks in-process with mocks. |
compile |
Multi-error validation pass — no scripts/ emission, no runtime spawn. |
format |
Rewrite .jh / .test.jh files into canonical style. |
init |
Initialize .jaiph/ directory layout in a workspace. |
install |
Install project-scoped libraries from the registry or git URLs. |
use |
Reinstall jaiph globally with a selected version or channel. |
{: #jaiph-run}
Compile and execute a workflow's default entrypoint.
jaiph run [--target <dir>] [--raw] [--workspace <dir>] [--inplace] [--unsafe] [--yes|-y] <file.jh> [--] [args...]
Sandbox selection is environment-driven; there is no --docker flag. The boolean sandbox flags (--inplace, --unsafe, --yes) are CLI front-ends that mutate the launched runtime env for one run only — see Configuration — Precedence and Environment variables.
| Flag | Argument | Effect |
|---|---|---|
--target |
<dir> |
Keep emitted script files and run metadata under <dir> instead of a temp directory. |
--raw |
— | Skip the banner, live progress tree, hooks, and PASS/FAIL footer. The runner child inherits stdio; __JAIPH_EVENT__ JSON lines go to stderr unchanged. Host --raw never launches Docker even when JAIPH_DOCKER_ENABLED=true. |
--workspace |
<dir> |
Override the workspace root used for library resolution and the Docker workspace mount. A missing value, missing path, or non-directory aborts with a specific message. There is no JAIPH_WORKSPACE env equivalent input — that name is reserved for the in-container remap output. |
--inplace |
— | Front-end for JAIPH_INPLACE=1. |
--unsafe |
— | Front-end for JAIPH_UNSAFE=true. Cannot be combined with --inplace (E_FLAG_CONFLICT). |
-y, --yes |
— | Front-end for JAIPH_INPLACE_YES=1. Required to use --inplace non-interactively. |
-- |
— | End of Jaiph flags; remaining tokens are forwarded to workflow default. |
After module-graph load and Docker-mode resolution, before the runner / container is spawned, the host CLI runs a credential pre-flight (src/cli/run/preflight-credentials.ts). Missing credentials produce either E_AGENT_CREDENTIALS (hard error) or a warning depending on backend and Docker mode — see Authenticate agent backends and Configuration — Credential pre-flight. jaiph run --raw does not run the pre-flight.
| Marker | Meaning |
|---|---|
▸ |
Step started. |
✓ |
Step completed successfully (with elapsed time). |
✗ |
Step failed (with elapsed time). |
ℹ |
log message (dim/gray, no marker timing). |
! |
logerr message (red; rendered on stdout with the progress tree). |
· |
Continuation marker (heartbeat lines in non-TTY mode). |
₁, ₂, … |
Subscript prefix for run async branch numbering. |
PASS line: ✓ PASS workflow default (0.2s). TTY runs append a transient ▸ RUNNING workflow <name> (X.Xs) line that is replaced by the PASS/FAIL line on exit. --raw and non-TTY modes skip both. Disable color globally with NO_COLOR=1.
Non-TTY heartbeat cadence is controlled by JAIPH_NON_TTY_HEARTBEAT_FIRST_SEC (default 60) and JAIPH_NON_TTY_HEARTBEAT_INTERVAL_MS (default 30000, floor 250).
Step lines include the kind (workflow, prompt, script, rule) and name. Parameterised invocations append key="value" pairs in parentheses (positional params use 1=… / 2=…); whitespace is collapsed; values are truncated to 32 characters. Prompt step lines additionally show the backend name (or custom command basename) and the first 24 characters of the prompt body in quotes (full line capped at 96 characters).
When workflow default returns a value (success only), the runtime writes return_value.txt under the run directory. Interactive jaiph run prints that value on stdout after the PASS line, separated by a blank line. jaiph run --raw never prints it to stdout; the file alone is the contract.
Each run directory is <JAIPH_RUNS_DIR>/<YYYY-MM-DD>/<HH-MM-SS>-<source>/, UTC. <source> is JAIPH_SOURCE_FILE if set, otherwise the entry-file basename. Layout pinned in Architecture — Durable artifact layout.
Step .out files are written incrementally; consumers may tail -f them. .out / .err pairs are allocated at STEP_START with monotonic per-run sequence numbers (%06d-<safe_name>.out|.err).
Interactive jaiph run only (--raw omits this block). On non-zero exit, the CLI emits a stderr footer with Logs:, Summary:, out: / err: paths, and an Output of failed step: excerpt. The fields are resolved from the last STEP_END object with non-zero status in run_summary.jsonl; out_content / err_content are preferred over out_file / err_file. In Docker mode, container-internal /jaiph/run/* paths are remapped to host paths.
Hooks load from ~/.jaiph/hooks.json (global) and <project>/.jaiph/hooks.json (project-local; project overrides global per event). Hooks run on the host CLI process even in Docker mode. See Add a hook.
Execute *.test.jh blocks using the same NodeWorkflowRuntime as jaiph run, in-process, with mock support.
jaiph test # discover all *.test.jh under the workspace root
jaiph test <dir> # discover all *.test.jh recursively under <dir>
jaiph test <file.test.jh> # run a single test file
| Invocation | Workspace root detection |
|---|---|
jaiph test |
Walk up from process.cwd() until .jaiph or .git; falls back to process.cwd(). |
jaiph test <dir> |
Walk up from the resolved <dir>. |
jaiph test <file> |
Walk up from the test file's directory. |
Zero matches with no arguments (or with a directory containing no *.test.jh files) writes jaiph test: no *.test.jh files found (nothing to do) to stderr and exits 0. An explicit file path that does not exist or is not *.test.jh exits 1. Plain workflow files (*.jh without .test) are not supported as test entries. Extra positional tokens after the path are accepted but ignored.
Assertions: expect_contain, expect_equal, expect_not_contain — see Write & run tests.
{: #jaiph-compile}
Parse modules and run collectDiagnostics(graph) — the same per-module validator as jaiph run, but collecting every recoverable error instead of stopping at the first — without writing scripts/, without calling buildRuntimeGraph(), and without spawning the workflow runner.
jaiph compile [--json] [--workspace <dir>] <file.jh | directory> ...
At least one path is required. -h / --help must appear before the first path (they are not scanned after a path token, unlike other subcommands).
| Argument shape | Behaviour |
|---|---|
File path (*.jh or *.test.jh) |
Expanded to the transitive import closure. Each module in the union is parsed and validated once. |
| Directory path | Tree scanned for *.jh files; *.test.jh is skipped (use an explicit file path to validate a test module). Each non-test *.jh is treated as an entrypoint and its closure merged into the validation set. |
| Flag | Effect |
|---|---|
--json |
On success, print [] to stdout. On failure, print one JSON array of { file, line, col, code, message } diagnostics to stdout and exit 1. |
--workspace <dir> |
Override library resolution root for all reached modules. Without it, the workspace is auto-detected per path. |
Within each entry's import closure, diagnostics are sorted by (file, line, col); when multiple entry points are supplied, those batches are appended in discovery order (not re-sorted globally). Without --json, the same set is written to stderr as path:line:col CODE message lines. Any non-empty diagnostic set exits 1. Parser/loader failures abort the affected entry's closure with a single diagnostic for that entry; siblings continue.
Reformat .jh / .test.jh files into canonical style.
jaiph format [--check] [--indent <n>] <path.jh ...>
Paths must end with .jh. Formatting is idempotent. Comments and shebangs are preserved. Triple-quoted bodies, prompt blocks, and fenced script blocks emit verbatim — inner lines are not re-indented relative to the surrounding scope.
| Flag | Argument | Default | Effect |
|---|---|---|---|
--indent |
<n> |
2 |
Spaces per indent level. |
--check |
— | — | Verify without writing. Exit 0 when files match canonical form, 1 when any file would change. |
Top-level ordering: the formatter hoists import, config, and channel declarations to the top (in that order, preserving relative source order within each group). Other top-level definitions (const, rule, script, workflow, test) keep their relative source order. Comments before a hoisted construct move with it; comments before non-hoisted definitions stay in place.
Top-level const quoting: the source delimiter is preserved per binding. Quoted values stay quoted; bare tokens stay bare; """…""" values emit verbatim. The formatter does not toggle between styles based on value content.
Blank-line preservation: a single blank line between steps inside a workflow or rule body is preserved. Multiple consecutive blank lines collapse to one. Trailing blank lines before } are removed.
jaiph init [workspace-path]
Creates the following under the target workspace:
| File | Content |
|---|---|
.jaiph/.gitignore |
Two-line file listing runs and tmp. If the file exists and does not match, the command exits non-zero. |
.jaiph/bootstrap.jh |
Canonical bootstrap workflow; made executable. The body is a triple-quoted multiline prompt that asks the agent to scaffold workflows. |
.jaiph/SKILL.md |
Copy of the skill markdown shipped with this jaiph build (see JAIPH_SKILL_PATH). |
SKILL.md resolution order: JAIPH_SKILL_PATH (if set and the path exists) → install-relative paths (jaiph-skill.md next to the package tree, then docs/jaiph-skill.md next to the package) → docs/jaiph-skill.md under the current working directory → the embedded copy baked into the binary. There is no "skip and warn" path; the file is always written.
Install project-scoped libraries into .jaiph/libs/<name>/ under the workspace root. The workspace root is detected from process.cwd() (detectWorkspaceRoot — walks up until .jaiph or .git, with temp-directory guards).
jaiph install [--force] [<name[@version]> | <repo-url[@version]> ...]
jaiph install [--force] # restore from lockfile
| Flag | Effect |
|---|---|
--force |
Delete and re-clone existing libraries. Accepted anywhere in the argument list. |
| Argument shape | Resolution |
|---|---|
Bare registry name matching ^[A-Za-z0-9_-]+(@[A-Za-z0-9._+/-]+)?$ (no /, no :) |
Looked up in the registry index. Examples: jaiphlang, mylib@v1.2. |
| Anything else | Parsed as a git URL with optional trailing @<version>. Examples: https://github.com/you/queue-lib.git, git@github.com:org/repo.git@main. |
Each successful clone runs three checks before the lib counts as installed:
.jhmodule check — at least one*.jhfile must exist under the clone (recursive,.gitskipped). Failure removes the directory and aborts withlib "<name>" contains no .jh modules — not a jaiph library?. No lock entry written.- Commit capture —
git rev-parse HEADis recorded as the 40-charcommiton the lock entry. .gitstrip —<libDir>/.gitis removed recursively.
jaiph install with no positional args reads .jaiph/libs.lock and clones each entry. The registry is never contacted. If a lock entry carries a commit, the cloned HEAD must match it; on mismatch the directory is removed and the run fails with the locked vs cloned SHAs and the remedy. Lock entries without commit (older lockfiles) restore without the check.
Missing libraries are cloned with bounded concurrency (default 4 in flight). The warm-skip pass runs before any clone. Independent clone failures still propagate; failed libraries are not added to the lockfile.
| Aspect | Value |
|---|---|
| Source | JAIPH_REGISTRY (default https://jaiph.org/registry). |
| Loading | Loaded once per invocation when at least one positional argument is a bare name. URL-form installs and restore-from-lock never read the registry. |
| Disk paths | Values without a :// scheme, or starting with file://, are read from disk. Everything else is fetched via global fetch. |
| Index format | { "libs": { "<name>": { "url": "<git-url>", "description": "<string>" } } }. Each key must match ^[A-Za-z0-9_-]+$. Unknown per-entry keys are accepted and ignored. |
| Lookup errors | lib "<name>" not found in registry <source>, failed to read registry <source>: <cause>, failed to fetch registry <source>: HTTP <status>, failed to parse registry <source>: <cause>, failed to parse registry <source>: invalid name "<name>". |
.jaiph/libs.lock shape:
{
"libs": [
{
"name": "jaiphlang",
"url": "https://github.com/jaiphlang/jaiphlang.git",
"commit": "1a2b3c4d5e6f7890abcdef1234567890abcdef12"
},
{
"name": "queue-lib",
"url": "https://github.com/you/queue-lib.git",
"version": "v1.0",
"commit": "fedcba9876543210fedcba9876543210fedcba98"
}
]
}The lock entry stores the resolved clone URL so restore works without the registry. commit is written automatically after each successful clone.
Reinstall jaiph globally with the selected channel or version.
jaiph use <version|nightly>
| Argument | Effect |
|---|---|
nightly |
Reinstalls from the rolling nightly prerelease. |
<version> (e.g. 0.10.0) |
Reinstalls the release binary for tag v<version>. |
Implementation: re-invokes JAIPH_INSTALL_COMMAND (default curl -fsSL https://jaiph.org/install | bash) with JAIPH_REPO_REF set to nightly or v<version>. The installer downloads the matching per-platform binary plus SHA256SUMS, verifies the checksum, and replaces ~/.local/bin/jaiph (or JAIPH_BIN_DIR).
See Environment variables for the complete inventory. The variables most relevant to CLI behaviour:
JAIPH_DOCKER_ENABLED,JAIPH_UNSAFE,JAIPH_INPLACE,JAIPH_INPLACE_YES— sandbox enablement and mode.JAIPH_DOCKER_IMAGE,JAIPH_DOCKER_NETWORK,JAIPH_DOCKER_TIMEOUT— Docker mode parameters.JAIPH_NON_TTY_HEARTBEAT_FIRST_SEC,JAIPH_NON_TTY_HEARTBEAT_INTERVAL_MS— non-TTY progress cadence.JAIPH_RUNS_DIR,JAIPH_WORKSPACE,JAIPH_SOURCE_FILE— run-layout inputs.JAIPH_INSTALL_COMMAND,JAIPH_REGISTRY,JAIPH_SKILL_PATH— install / init inputs.NO_COLOR— disable ANSI colour output.
- Live contract (runtime → CLI):
__JAIPH_EVENT__JSON lines on stderr only. Hooks and the interactive progress tree consume this stream. Stdout carries plain script output forwarded as-is. - Durable contract:
.jaiph/runs/...+run_summary.jsonl+.out/.errstep artifacts + optionalreturn_value.txt. See Architecture — Durable artifact layout.
run_summary.jsonl event types: WORKFLOW_START, WORKFLOW_END, STEP_START, STEP_END, LOG, LOGERR, INBOX_ENQUEUE, INBOX_DISPATCH_START, INBOX_DISPATCH_COMPLETE, PROMPT_START, PROMPT_END. Every object carries type, ts (UTC), run_id, and event_version (currently 1). Step events also carry id, parent_id, seq, depth. See Architecture — Contracts.
.jh is the file extension for Jaiph source. Import resolution appends .jh when the path omits the extension. *.test.jh is the test-module convention recognised by jaiph test and file shorthand.
- Configuration — config keys, precedence, scoping.
- Grammar — syntax and validation catalog.
- Language — step semantics and step-output contract.
- Environment variables — every variable Jaiph reads.