Your First Workflow
JJHub workflows are TSX files stored in your repository under.jjhub/workflows/. They use Smithers JSX components wrapped by the @jjhub-ai/workflow package.
Project Setup
The.jjhub/ directory at your repository root is a Bun workspace member with its own package.json and tsconfig.json. This gives you full LSP support (autocomplete, type checking, go-to-definition) when editing workflows.
.jjhub/package.json
.jjhub/tsconfig.json
Root package.json
Add .jjhub and packages/* to your Bun workspaces so the @jjhub-ai/workflow dependency resolves:
bun install to link the workspace.
The @jjhub-ai/workflow package lives at packages/workflow/ and re-exports Smithers JSX components (Workflow, Task, Parallel, Sequence, Branch, Ralph) plus the on trigger builders.
Create a Workflow File
Coming Soon: We are actively working on a first-class feature to allow you to run these exact same workflows entirely on your local machine for rapid testing and debugging! Check our Roadmap for more details.
Trigger a Run
Workflows run automatically on configured events (push, landing request opened, schedule, etc.). To manually trigger a workflow:Manual Dispatch with Inputs
Workflows can declare inputs that users provide when triggering a run manually. This is useful for deploy workflows, release workflows, or any workflow that needs runtime parameters.Defining Inputs
Use theon.manualDispatch trigger to declare the inputs your workflow accepts:
Input Types
| Type | Description | Example |
|---|---|---|
string | Free-form text | version: { type: "string" } |
boolean | True or false | dry_run: { type: "boolean", default: false } |
number | Numeric value | replicas: { type: "number", default: 3 } |
choice | One of a set of allowed values | environment: { type: "choice", options: ["staging", "production"] } |
| Property | Type | Description |
|---|---|---|
type | string | Required. One of string, boolean, number, choice. |
required | boolean | Whether the input must be provided. Default: false. |
default | varies | Default value when the input is not provided. |
description | string | Human-readable description shown in error messages and documentation. |
options | string[] | Required for choice type. The allowed values. |
Triggering via CLI
Pass inputs using the--input (or -i) flag, one per key-value pair:
choice input:
Triggering via API
Send aPOST request to the workflow dispatches endpoint with inputs in the request body:
| Method | Endpoint | Description |
|---|---|---|
POST | /api/repos/:owner/:repo/workflows/:workflow_id/dispatches | Trigger a manual workflow dispatch with inputs |
| Field | Type | Description |
|---|---|---|
ref | string | Git ref to run on (default: main) |
inputs | object | Key-value map of input names to values |
Accessing Inputs in Workflow Code
Inside any task, access inputs viactx.input:
ctx.input is an empty object.
View Runs
Scheduled Triggers
Workflows can run on a schedule using CRON expressions. This is useful for nightly builds, periodic reports, dependency checks, and any recurring automation.Defining a Schedule
Useon.schedule() in your triggers array with a standard CRON expression:
CRON Syntax
Schedules use standard five-field CRON syntax:Common Patterns
| Pattern | CRON Expression | Description |
|---|---|---|
| Nightly at midnight | 0 0 * * * | Runs once per day at 00:00 UTC |
| Every 6 hours | 0 */6 * * * | Runs at 00:00, 06:00, 12:00, 18:00 UTC |
| Hourly | 0 * * * * | Runs at the start of every hour |
| Weekly on Monday 9am | 0 9 * * 1 | Runs every Monday at 09:00 UTC |
| Weekdays at 8am | 0 8 * * 1-5 | Runs Monday through Friday at 08:00 UTC |
Timezone
All schedules run in UTC. Adjust your CRON expressions accordingly. For example, if you want a nightly build at midnight US Eastern (UTC-5), use0 5 * * *.
Multiple Schedules
A workflow can have multiple schedule triggers, and can combine schedules with other trigger types:How Scheduled Workflows Run
Scheduled workflows are managed by the JJHub scheduler on the server side, not triggered by push events or user actions. When a schedule fires:- The scheduler matches the CRON expression against the current UTC time
- A workflow run is created targeting the default bookmark (typically
main) - The run is queued and assigned to a runner pod from the warm pool
- Execution proceeds identically to any other workflow run (same logs, SSE streaming, commit statuses)
AI Agent Steps
Workflows can invoke AI agents as tasks using theagent prop:
Schema-Driven Workflows
For AI agent workflows that pass structured data between tasks, usecreateSmithers to define output schemas with Zod. This gives you type-safe ctx.output() for reading previous task results and automatic SQLite-backed persistence.
How It Works
| Concept | Description |
|---|---|
createSmithers({ ... }) | Defines Zod schemas for task outputs. Returns Workflow, Task, and smithers bound to those schemas. |
smithers((ctx) => ...) | Wraps your workflow factory with schema-aware context. Use instead of a plain export default (ctx) => .... |
output="analysis" | Tells the task to validate and persist its output against the analysis Zod schema. |
ctx.output("analysis", { nodeId: "analyze" }) | Reads the typed output from a previous task. Fully type-safe — returns { summary: string, severity: "low" | "medium" | "high" }. |
export default (ctx) => <Workflow> pattern (shown earlier) works for compute tasks that just run shell commands. Use createSmithers when tasks need to pass structured data to each other.
Next Steps
- AI Agents Guide - Workflow agents, sandboxed runners, and workspaces
- Landing Requests Guide - full LR lifecycle
- CLI Reference - all available commands