Scaffolding a package

September 5, 2026 · View on GitHub

What this is: the finer points of npm create @malloy-publisher/malloy-package, the scaffolder the README's Start from your own data uses. Read it when the short version left a question.

Keep the @latest

npm create resolves the scaffolder through npm's npx cache. On a machine that has run the command before, an unversioned name is satisfied by whatever copy is already cached, so npm never asks the registry — and you can quietly scaffold from a months-old scaffolder that pins an older server than the one you meant to run. The scaffolder checks its own version against the registry once it has finished writing and tells you when it is behind; that check is bounded and fails open, is skipped where CI or NO_UPDATE_NOTIFIER is set, and CREATE_MALLOY_PACKAGE_NO_UPDATE_CHECK=1 turns it off anywhere else.

Make the directory first

The package lands in ./sales, but the workspace around it is written to the current directory, so running the command somewhere you did not mean to scatters config files through it. The workspace is: start and reset scripts, an MCP config, agent instructions, and the Malloy agent skills as files your agent can read. npm start runs the server version the scaffolder pinned, against your package, in watch mode.

Sample data, or your own

Run bare, the package comes with a small sample dataset, so there is something to query before you have wired up anything of your own. --data seeds it from a file instead — CSV, Parquet, JSON, newline-delimited JSON, or Excel .xlsx; DuckDB reads all of them in place, so nothing needs converting. Any plain delimited file with a header row works:

order_id,category,amount
1001,Furniture,789
1002,Electronics,489.95

The path is relative to the directory you run the command in; the scaffolder copies the file into the package, so the original stays where it is. Pasting ./orders.csv verbatim fails if no such file exists.

The -- before --data is required. Without it, npm create reads --data as one of its own options and only the filename reaches the scaffolder, as a stray argument, so it stops.

A seeded package starts smaller than the sample one: the scaffolder does not read your columns, so you get a row count and an overview over your file, and the modelling starts there. That is the point at which pointing an agent at the workspace pays off.

Beyond a local file

A package is just Malloy, so it is not limited to the file it was seeded from: point its model at a database connection your config defines and the same workspace serves a warehouse. The directory is ordinary — commit it, move it, or hand it to someone else.

Which config the server reads

With the workspace in place, the server serves your package rather than the bundled examples: npm start points it at the publisher.config.json the scaffolder wrote, and a bare npx @malloy-publisher/server run from this directory picks up the same file. The README's agent walkthrough is written against the examples, so run that from a directory without this config.

Without npm create

Call the package by its full name:

npx @malloy-publisher/create-malloy-package@latest sales --data ./orders.csv

The name is create-malloy-package here, where npm create takes the malloy-package shorthand. The same caching applies, so @latest is worth keeping — and npx needs no separator: it forwards flags as they are, so a -- there leaves the flags after it to arrive as stray arguments.