Skip to content

Project Structure

A stepbook project is just a directory with a config file, an entrypoint, and some step files. There's no hidden global state — everything lives in your repo.

Anatomy

my-pipeline/
├── stepbook.config.json    # names the pipeline, points at the entrypoint + steps
├── pipeline.ts             # the entrypoint — definePipeline({ name, runner })
├── steps/
│   ├── parse.step.ts        # one file per unit, default-exported defineStep({...})
│   └── analyze.step.ts
├── cases/                  # named fixed inputs for running a unit in isolation
│   └── parse/
│       └── hello.json
├── inputs/
│   ├── hello.json           # sample inputs to run the pipeline against
│   └── sample.md
└── state/                  # gitignored — runs, snapshot cache, reports
    ├── runs/                # one JSON blob per run
    ├── runs.jsonl           # append-only run index (totals, timestamps, SHAs)
    ├── snapshots/           # file-based snapshot cache
    └── reports/             # Markdown reports from --md

stepbook.config.json

The config names the pipeline and tells stepbook where to find its entrypoint and step files. The config used by the demo:

json
{
  "name": "demo",
  "steps": ["steps/**/*.step.ts"],
  "pipelineFile": "pipeline.ts"
}
  • pipelineFile — path to the entrypoint that stepbook run executes.
  • steps — a glob (or array of globs) for your step files, so stepbook can load and register each unit by name (this is what lets ctx.run('parse', …) and the dashboard resolve a unit).

At least one of steps or pipelineFile must be set. See the Config Reference for every field.

Config formats

stepbook.config.json, stepbook.config.mjs, and stepbook.config.js are all supported. JSON is simplest; the .mjs/.js forms let you compute values.

pipeline.ts — the entrypoint

The default export of pipeline.ts is a definePipeline({ name, runner }). Its runner composes your units in code — a line for a pipeline, a loop for an agent. This is what stepbook run executes.

ts
import { definePipeline } from '@stepbook/runner'
import parse from './steps/parse.step'
import analyze from './steps/analyze.step'

export default definePipeline({
  name: 'demo',
  runner: async (input) => {
    const parsed = await parse(input)
    return analyze(parsed)
  },
})

An entrypoint can also declare beforeAll / afterAll hooks that fire around an entire run. See Pipelines & Trajectories.

steps/*.step.ts — units

One file per unit. Each file's default export is a defineStep({...}) call. Stepbook globs these at load time (via the steps glob) and registers each by name — inferred from the filename when you don't set one, so 03-analyze.step.ts registers the unit analyze.

ts
import { defineStep } from '@stepbook/runner'

export default defineStep({
  type: 'parse',
  runner: async (input) => ({ words: input.text.split(/\s+/) }),
})

Files matching the glob that don't default-export a step registration (helpers, fixtures, colocated tests) are silently skipped, so you can keep related code next to your units.

cases/<step>/*.json

A case is a named, fixed input for running a single unit in isolation — a "story" for a step. A cases/parse/hello.json file supplies an input for the parse unit:

json
{ "input": { "text": "hello world" } }

Cases can also be authored as named exports inside the step file. See Steps & Step Types.

inputs/*.json

Sample inputs the whole pipeline runs against. stepbook run with no argument uses the canonical input — the first file in inputs/, or the one named by canonicalInput in your config. Pass a specific file with stepbook run inputs/other.json.

state/ — your local data

Everything stepbook persists goes under state/, which you should gitignore. It holds:

  • runs/ — one JSON blob per run (the full trajectory: inputs, outputs, checks, telemetry).
  • runs.jsonl — an append-only index of runs with totals, timestamps, and git SHAs. This is what stepbook list and the History panel read.
  • snapshots/ — the file-based snapshot cache, keyed per node by its input + config + source hash. See The Cache.
  • reports/ — Markdown reports written by commands run with --md.

Because it's all local files, you can inspect it directly, delete it to start fresh, or stepbook prune it to trim old runs.

Packages

Stepbook itself is a small monorepo. As a user you mostly interact with the CLI and the dashboard, but it's useful to know the layers:

PackagePurpose
@stepbook/runnerExecution engine. Pure Node, no UI. Composition, cache/replay, assertions, persistence, similarity.
@stepbook/queryQuery engine: the JSONata evaluator plus a structured query AST that compiles to JSONata.
@stepbook/cliThe stepbook command. Built on the runner.
@stepbook/dashboardReact + Vite UI. Calls into the runner via a Vite dev-server plugin.
@stepbook/plugin-apiPublic types for third-party dashboard plugins. No runtime.

Step files import defineStep (and friends) from @stepbook/runner; the entrypoint imports definePipeline.

Released under the MIT License.