| name | add-pipeline-story |
| description | Add a new pipeline example to the Storybook from a .mthds bundle file. Generates both dry-run and live-run GraphSpecs via pipelex CLI, creates the data directory, spec file, wires it into mockGraphSpec.ts, and creates the story file. Use when user says "add example", "add pipeline", "new storybook story", "add a graph", "new pipeline example", or provides a .mthds file to visualize.
|
| user_invocable | true |
Add Pipeline Story
Add a new pipeline example to the mthds-ui Storybook from a .mthds bundle file.
Always generates BOTH dry-run and live-run GraphSpecs.
Prerequisites
pipelex CLI must be on PATH
mthds-agent CLI must be on PATH (for input generation)
- The
.mthds file must be accessible from the workspace
Workflow
1. Identify the .mthds file
Ask the user for the path to the .mthds bundle if not provided.
Verify the file exists.
2. Determine the next pipeline number
ls data/pipelines/ | grep pipeline | sort -t_ -k2 -n | tail -1
Extract the number (e.g., pipeline_30 -> next is pipeline_31).
The story file number matches: Pipeline31.stories.tsx.
3. Choose a spec name
Derive a spec name from the bundle filename or ask the user.
- Bundle
cv_analyzer.mthds -> spec name cvAnalyzer, const prefix CV_ANALYZER
- Bundle
report_writer/bundle.mthds -> spec name reportWriter, const prefix REPORT_WRITER
- Use camelCase for filename, UPPER_SNAKE_CASE for const prefix
4. Create the data directory
mkdir -p data/pipelines/pipeline_<NN>
cp <path-to-bundle.mthds> data/pipelines/pipeline_<NN>/bundle.mthds
5. Generate inputs
Use mthds-agent to discover what inputs the bundle requires:
mthds-agent inputs bundle data/pipelines/pipeline_<NN>/bundle.mthds -L data/pipelines/pipeline_<NN>/
This outputs a JSON structure showing required inputs with their concepts.
The generated URLs are mock URLs (https://mock-*.invalid/...).
Replace mock URLs with real, accessible test files. Use files from:
https://pipelex-web.s3.amazonaws.com/demo/ for PDFs (John-Doe-CV.pdf, Job-Offer.pdf)
https://pipelex-web.s3.us-west-2.amazonaws.com/tests/ for images (alan_turing.jpg, eiffel_tower.jpg)
- Or ask the user for specific input files
Save the inputs:
cat > data/pipelines/pipeline_<NN>/inputs.json << 'EOF'
{
...inputs JSON with real URLs...
}
EOF
6. Generate GraphSpecs (ALWAYS both dry-run AND live-run)
Dry run:
pipelex run bundle data/pipelines/pipeline_<NN>/bundle.mthds \
--dry-run --mock-inputs --graph \
-o /tmp/pipeline<NN>_dry
Copy the output:
cp /tmp/pipeline<NN>_dry/<output_dir>/graphspec.json \
data/pipelines/pipeline_<NN>/dry_run_graph_spec.json
Live run:
pipelex run bundle data/pipelines/pipeline_<NN>/bundle.mthds \
--graph \
-i data/pipelines/pipeline_<NN>/inputs.json \
-o /tmp/pipeline<NN>_live
Copy the output:
cp /tmp/pipeline<NN>_live/<output_dir>/graphspec.json \
data/pipelines/pipeline_<NN>/live_run_graph_spec.json
7. Create the spec file
Create src/graph/react/viewer/__stories__/pipelines/specs/<specName>.ts:
Use Python to convert JSON to TypeScript (handles null/undefined properly):
python3 -c "
import json
with open('data/pipelines/pipeline_<NN>/dry_run_graph_spec.json') as f:
dry = json.load(f)
with open('data/pipelines/pipeline_<NN>/live_run_graph_spec.json') as f:
live = json.load(f)
content = 'import type { GraphSpec } from \"@graph/types\";\n\n'
content += '// Generated by: pipelex run bundle data/pipelines/pipeline_<NN>/bundle.mthds\n'
content += '// Do not edit manually โ regenerate by running the pipeline.\n\n'
content += 'export const DRY_<CONST_PREFIX>: GraphSpec = ' + json.dumps(dry, indent=2, ensure_ascii=False) + ' as any as GraphSpec;\n\n'
content += 'export const LIVE_<CONST_PREFIX>: GraphSpec = ' + json.dumps(live, indent=2, ensure_ascii=False) + ' as any as GraphSpec;\n'
with open('src/graph/react/viewer/__stories__/pipelines/specs/<specName>.ts', 'w') as f:
f.write(content)
"
The as any as GraphSpec cast handles null values from pipelex output that
TypeScript types define as undefined.
Run prettier on the file:
npx prettier --write src/graph/react/viewer/__stories__/pipelines/specs/<specName>.ts
8. Wire into mockGraphSpec.ts
Edit src/graph/react/viewer/__stories__/mockGraphSpec.ts:
-
Add import after the last import block:
import { DRY_<CONST_PREFIX>, LIVE_<CONST_PREFIX> } from "./pipelines/specs/<specName>";
-
Add to the re-export block (inside export { ... }):
DRY_<CONST_PREFIX>,
LIVE_<CONST_PREFIX>,
9. Create the story file
Create src/graph/react/viewer/__stories__/pipelines/Pipeline<NN>.stories.tsx:
import type { Meta, StoryObj } from "@storybook/react-vite";
import { GraphViewer } from "../../GraphViewer";
import { DRY_<CONST_PREFIX>, LIVE_<CONST_PREFIX> } from "../mockGraphSpec";
const meta: Meta<typeof GraphViewer> = {
title: "Graph/GraphViewer/<NN> <Human-Readable Title>",
component: GraphViewer,
decorators: [
(Story) => (
<div style={{ width: "100%", height: "100vh", position: "relative" }}>
<Story />
</div>
),
],
argTypes: {
direction: { control: { type: "inline-radio" }, options: ["LR", "TB"] },
showControllers: { control: { type: "boolean" } },
},
};
export default meta;
type Story = StoryObj<typeof GraphViewer>;
const D = { direction: "LR" as const, showControllers: true };
export const DryRun: Story = {
args: { graphspec: DRY_<CONST_PREFIX>, ...D },
};
export const LiveRun: Story = {
args: { graphspec: LIVE_<CONST_PREFIX>, ...D },
};
10. Validate
make check
Fix any lint/format/type errors before finishing.
11. Report
Tell the user:
- The data directory:
data/pipelines/pipeline_<NN>/
- The Storybook story:
Graph/GraphViewer/<NN> <Title>
- How to view:
make storybook then navigate to the story
- Remind: never hand-edit spec files, regenerate with
pipelex run bundle
Data directory structure
Each pipeline example lives in data/pipelines/pipeline_<NN>/:
data/pipelines/pipeline_<NN>/
bundle.mthds # The source .mthds bundle
inputs.json # Inputs for the live run
dry_run_graph_spec.json # GraphSpec from dry run
live_run_graph_spec.json # GraphSpec from live run
This is the source of truth. The TypeScript spec files in
src/graph/react/viewer/__stories__/pipelines/specs/ are derived from these
JSON files and can be regenerated at any time.