Tooling

Write, validate, and visualize flows anywhere

Pipedown is a portable format — it doesn’t live inside start.chat. You can write .pd files in your own editor, validate them in CI, visualize them as diagrams, and even transpile them to TypeScript or Mastra workflows.

This page covers everything you need to work with pipedown outside of start.chat: the CLI, editor support, configuration, and syntax highlighting integrations. For how start.chat executes pipedown programs at runtime, see How it Runs.

CLI (@pipedown/cli)

The command-line tool for validating, visualizing, building, and generating code from .pd files. Uses citty under the hood.

Terminal

npm install -g @pipedown/cli
# or per-project
npm install --save-dev @pipedown/cli

pipedown validate [file]

Use this to catch syntax errors and structural issues before they hit production — works great as a CI gate.

Parses one .pd file or recursively scans cwd for all .pd files (skips node_modules/ and dist/).

Checks performed:

  • Parse errors (syntax)
  • No steps found in flow (warning)
  • Slash command missing action (error)
  • Slash command with no message text (warning)
  • each block references a variable that doesn’t exist (warning)
  • --- separator at the very start or end (warning)

Exit code 1 when any issues are found, so it works in CI:

$ pipedown validate flows/deploy-notify.pd flows/weekly-report.pd:14 each references 'missing' but no block or variable produces that name flows/bad.pd:1 parse error: unexpected token 3 file(s), 2 issue(s)

pipedown graph <file> [--format mermaid|html] [--open]

Use this to see the shape of a flow at a glance — especially helpful for debugging complex parallel or each structures.

Converts a .pd file to a Mermaid graph TD diagram.

  • Default format is html — writes a self-contained HTML page to a temp file and opens it in the system browser
  • --format mermaid prints raw Mermaid source to stdout
  • --no-open suppresses auto-open

Visual conventions: trigger as green stadium node, LLM steps purple, app steps blue, separator as a barrier circle, parallel steps fan out directly, each blocks as subgraphs.

pipedown generate --language <target> [--file <file>] [--outDir <dir>]

Use this to export your flows as real code — useful for migrating to another platform or running flows outside of start.chat entirely.

Transpiles .pd files to runnable code in a target language.

Targets:

  • typescript / ts — async functions wrapping PipedownRuntime calls. Sequential steps become await runtime.app(...), parallel blocks become Promise.all, each blocks become for loops.
  • mastra — Mastra workflow DSL (createWorkflow / createStep from @mastra/core). App steps map to createStep, LLM steps to prompt steps, schedule triggers to trigger: { schedule: '...' }.

When --outDir is given, writes one file per input .pd file. Without it, prints all generated output to stdout.

New targets can be added by implementing (content: string, filePath: string) => string and registering in the generators map.

pipedown build [--outDir dist]

Use this to pre-parse flows for deployment — feed the JSON AST into a custom runtime or a CI artifact.

Parses every .pd file in cwd recursively and writes one .json file per flow to outDir/ (default: dist/). The JSON output is the parsed PipedownProgram AST:

$ pipedown build flows/weekly-report.pd -> dist/weekly-report.json (6 steps) flows/deploy-notify.pd -> dist/deploy-notify.json (4 steps) built 2 flow(s) to dist/

VSCode extension (pipedown-vscode)

Use this for first-class .pd editing in VSCode — syntax highlighting, completions, and diagnostics without leaving your editor.

Registers .pd files as the pipedown language. Activates via onLanguage:pipedown.

Features:

  • Language registration: .pd extension, aliases Pipedown, pipedown, pd
  • Syntax highlighting via TextMate grammar (source.pipedown)
  • Markdown code block injection — ```pd fences in .md files get highlighting too
  • LSP client connecting to @pipedown/lsp for diagnostics, completions, hover

The extension uses vscode-languageclient with TransportKind.ipc to start the LSP server in-process.

LSP (@pipedown/lsp)

Use this for editor-agnostic intelligence — any editor that speaks LSP (Neovim, Helix, Zed, etc.) gets diagnostics, completions, and hover info.

Implemented with vscode-languageserver. Server capabilities:

  • textDocumentSync: Incremental
  • completionProvider — trigger characters: . : "
  • hoverProvider

Completions

Currently in progress — the completion logic is implemented in @pipedown/core’s createCompletionProvider() (Monaco) and being ported to the LSP. The Monaco provider (reference implementation) supports:

  • / at start of line — suggests all registered apps
  • /app — suggests actions for that app with descriptions
  • on — suggests apps that have events
  • on app. — suggests events for that app
  • Empty line — suggests starting patterns: * /, /, ---, each, every, on

Hover

In progress — planned to show action descriptions from the app registry on hover over /app action tokens.

Diagnostics

The plumbing is in place (validateDocument fires on every content change) but the check logic from pipedown validate hasn’t been ported yet.

Using the LSP standalone

For editors other than VSCode (Neovim, Helix, Zed, etc.), create a small wrapper:

// lsp-server.mjs
import { startServer } from '@pipedown/lsp'
startServer()
-- nvim-lspconfig example
{
cmd = { "node", "/path/to/lsp-server.mjs" },
filetypes = { "pipedown" },
root_dir = require("lspconfig.util").root_pattern("pipedown.config.ts", ".git"),
}

The wrapper must use stdio transport — the VSCode extension uses IPC internally.

Configuration (pipedown.config.ts)

Drop a pipedown.config.ts at the root of your project and all CLI commands will pick it up automatically. Flags override config values when both are present.

import { defineConfig } from '@pipedown/core/config'
export default defineConfig({
include: ['./flows/**/*.pd'],
exclude: [],
outDir: 'dist',
graph: {
format: 'svg',
theme: 'dark',
},
generate: {
languages: [{ name: 'mastra', outDir: 'dist/mastra' }],
},
})
FieldTypeDefaultDescription
includestring[]all **/*.pdGlob patterns for input
excludestring[]noneGlob patterns to skip
outDirstring'dist'Output dir for build
graph.format'svg' | 'png' | 'mermaid'Graph output format
graph.theme'dark' | 'light'Graph color theme
generate.languagesArray<{ name, outDir? }>Code generation targets

Syntax highlighting

Pipedown ships highlighting integrations for static sites (Shiki), in-browser editors (Monaco), and any editor that supports TextMate grammars.

Shiki (static / SSG)

Use this for docs sites and static rendering — drop in the grammar and highlight .pd code blocks at build time.

import { createHighlighter } from 'shiki'
import { pipedownLanguage } from '@pipedown/core/shiki'
const highlighter = await createHighlighter({
langs: [...pipedownLanguage],
themes: ['dark-plus'],
})
const html = highlighter.codeToHtml(source, { lang: 'pipedown' })

pipedownLanguage is an array containing the grammar object with id: 'pipedown' and aliases ['pd', 'pipedown'], derived from the TextMate grammar.

Monaco (in-browser editor)

Use this for browser-based flow editors — gives you tokenization, completions, and theming out of the box.

import * as monaco from 'monaco-editor'
import { registerPipedownLanguage, configurePipedownApps } from '@pipedown/core/monaco'
// replace the default app list before registering
configurePipedownApps([
{
name: 'myapp',
description: 'My integration',
actions: [{ name: 'fetch' }],
},
])
registerPipedownLanguage(monaco)
monaco.editor.create(document.getElementById('editor'), {
language: 'pipedown',
theme: 'pipedown-dark',
})

registerPipedownLanguage wires up: Monarch tokenizer, language configuration (/[^\s]+/ word pattern, no auto-closing pairs), completion provider (apps, actions, events, keywords with / and space as trigger characters), and pipedown-dark/pipedown-light themes.

configurePipedownApps replaces the entire registry (does not append). Call it before registerPipedownLanguage or call it again to update completions at runtime.

TextMate grammar

Located at packages/pipedown/src/pipedown.tmLanguage.json. Scope: source.pipedown.

PatternScope
# name: / ## desccomment.line / string.unquoted
# Title headingentity.name.section
--- separatorkeyword.control
every / on triggerkeyword.control
name = variable blockentity.name.section
each keyword + refskeyword.control + entity.name.tag
* parallel prefixkeyword.operator.parallel
- sequential prefixpunctuation.definition.list
/app slash + app nameentity.name.function
/app action action nameentity.name.tag
{{variable}} referencesvariable.other
@mention referencesentity.name.tag
Remaining textstring.unquoted

The grammar is shared between the VSCode extension and the Shiki export. The extension also injects pipedown highlighting into Markdown fenced code blocks.

Example project

packages/pipedown-example shows a multi-flow project structure with everything wired together:

pipedown-example/ pipedown.config.ts flows/ weekly-report.pd # scheduled, parallel blocks, each + variables deploy-notify.pd # simple sequential: github -> llm -> slack -> linear onboarding.pd # event-driven: stripe -> postmark -> llm -> slack data-sync.pd # parallel fetch, barrier, each iteration

Terminal

cd packages/pipedown-example
pipedown validate # validate all flows
pipedown build # build to dist/
pipedown graph flows/weekly-report.pd # visualize a flow
pipedown generate --language typescript --outDir dist/ts # generate TS
pipedown generate --language mastra --outDir dist/mastra # generate Mastra