
Jaiph is a scripting, declarative, and composable language to define AI agent workflows.
It combines the power of declarative languages with bash, to create portable, and easy to understand AI agent workflows. It compiles to pure bash, making it fully compatible with existing shell environments.
⚠ Jaiph is still in an early stage.
You may expect breaking changes. You may also have a great impact as an early contributor.
import "security.jph" as security
import "bootstrap_project.jph" as bootstrap
# Validates local build prerequisites.
rule project_ready {
test -f "package.json"
test -n "$NODE_ENV"
}
# Verifies the project compiles successfully.
rule build_passes {
npm run build
}
# Orchestrates checks, prompt execution, and docs refresh.
# Arguments:
# $1: Feature requirements passed to the prompt.
workflow main {
if ! ensure project_ready; then
run bootstrap.nodejs
fi
prompt "
Build the application using best practices.
Follow requirements: $1
"
ensure build_passes
ensure security.scan_passes
run update_docs
}
# Refreshes documentation after a successful build.
workflow update_docs {
prompt "Update docs"
}
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/jaiph_stdlib.sh"
source "$(dirname "${BASH_SOURCE[0]}")/bootstrap_project.sh"
source "$(dirname "${BASH_SOURCE[0]}")/tools/security.sh"
# Validates local build prerequisites.
main__rule_project_ready__impl() {
test -f "package.json"
test -n "$NODE_ENV"
}
main__rule_project_ready() {
jaiph__run_step main__rule_project_ready jaiph__execute_readonly main__rule_project_ready__impl
}
# Verifies the project compiles successfully.
main__rule_build_passes__impl() {
npm run build
}
main__rule_build_passes() {
jaiph__run_step main__rule_build_passes jaiph__execute_readonly main__rule_build_passes__impl
}
# Orchestrates checks, prompt execution, and docs refresh.
# Arguments:
# $1: Feature requirements passed to the prompt.
main__workflow_main__impl() {
if ! main__rule_project_ready; then
bootstrap_project__workflow_nodejs
fi
jaiph__prompt "
Build the application using best practices.
Follow requirements: $1
"
main__rule_build_passes
tools__security__rule_scan_passes
main__workflow_update_docs
}
main__workflow_main() {
jaiph__run_step main__workflow_main main__workflow_main__impl "$@"
}
# Refreshes documentation after a successful build.
main__workflow_update_docs__impl() {
jaiph__prompt "Update docs"
}
main__workflow_update_docs() {
jaiph__run_step main__workflow_update_docs main__workflow_update_docs__impl "$@"
}
#!/usr/bin/env bash
# Standard helpers shared by transpiled Jaiph modules.
jaiph__version() {
echo "jaiph 0.0.1"
}
jaiph__die() {
local message="$1"
echo "jai: $message" >&2
return 1
}
jaiph__prompt() {
cursor-agent "$@"
}
jaiph__new_run_id() {
if command -v uuidgen >/dev/null 2>&1; then
uuidgen | tr "[:upper:]" "[:lower:]"
return 0
fi
printf "%s-%s-%s" "$$" "$RANDOM" "$(date +%s)"
}
jaiph__sanitize_name() {
local raw="$1"
raw="${raw//[^[:alnum:]_.-]/_}"
printf "%s" "$raw"
}
jaiph__init_run_tracking() {
if [[ -n "${JAIPH_RUN_DIR:-}" ]]; then
return 0
fi
local started_at run_id
started_at="$(date -u +"%Y%m%dT%H%M%SZ")"
run_id="$(jaiph__new_run_id)"
JAIPH_RUN_DIR="$PWD/${started_at}-${run_id}"
JAIPH_PRECEDING_FILES=""
mkdir -p "$JAIPH_RUN_DIR"
export JAIPH_RUN_DIR JAIPH_PRECEDING_FILES
}
jaiph__track_output_files() {
local out_file="$1"
local err_file="$2"
if [[ -z "$JAIPH_PRECEDING_FILES" ]]; then
JAIPH_PRECEDING_FILES="${out_file},${err_file}"
else
JAIPH_PRECEDING_FILES="${JAIPH_PRECEDING_FILES},${out_file},${err_file}"
fi
export JAIPH_PRECEDING_FILES
}
jaiph__run_step() {
local func_name="$1"
shift || true
if [[ -z "$func_name" ]]; then
jaiph__die "jaiph__run_step requires a function name"
return 1
fi
if [[ "$#" -eq 0 ]]; then
jaiph__die "jaiph__run_step requires a command to execute"
return 1
fi
jaiph__init_run_tracking || return 1
local step_started_at safe_name out_file err_file status
step_started_at="$(date -u +"%Y%m%dT%H%M%SZ")"
safe_name="$(jaiph__sanitize_name "$func_name")"
out_file="$JAIPH_RUN_DIR/${step_started_at}-${safe_name}.out"
err_file="$JAIPH_RUN_DIR/${step_started_at}-${safe_name}.err"
"$@" >"$out_file" 2>"$err_file"
status=$?
jaiph__track_output_files "$out_file" "$err_file"
if [[ -s "$out_file" ]]; then
cat "$out_file"
fi
if [[ -s "$err_file" ]]; then
cat "$err_file" >&2
fi
return "$status"
}
# Wrapper to execute functions in a read-only filesystem sandbox.
jaiph__execute_readonly() {
local func_name="$1"
if [[ -z "$func_name" ]]; then
jaiph__die "jaiph__execute_readonly requires a function name"
return 1
fi
if ! declare -f "$func_name" >/dev/null 2>&1; then
jaiph__die "unknown function: $func_name"
return 1
fi
if ! command -v unshare >/dev/null 2>&1; then
jaiph__die "unshare is required for read-only rule execution"
return 1
fi
if ! command -v sudo >/dev/null 2>&1; then
jaiph__die "sudo is required for read-only rule execution"
return 1
fi
export -f "$func_name"
export -f jaiph__die
export -f jaiph__prompt
sudo env JAIPH_PRECEDING_FILES="$JAIPH_PRECEDING_FILES" unshare -m bash -c "
mount --make-rprivate /
mount -o remount,ro /
$func_name
"
}
The installation below uses Jaiph from the main branch in the GitHub repository: https://github.com/jaiphlang/jaiph. The specification is still in flux and may break in the future. You may expected breaking changes.
curl -fsSL https://jaiph.org/install | bash
Then verify the installation:
jaiph --version
In case of error, check if ~/.local/bin is in your PATH. This is the default
installation directory.
jaiph run path/to/main.jph "feature request or task"
import "file.jph" as aliasrule name { ... }workflow name { ... }ensure refrun refprompt "..."All language primitives can be freely combined with any valid Bash code—they are not isolated or blocking, but instead are fully interoperable with standard shell scripting within your workflows and rules.
Open GitHub Docs