Skip to content

Scaffolding a project ​

create-skein-js generates a working skein-js project from an empty directory. It is the fastest path from nothing to a running agent server, and it is optional — everything it emits is a file you could write yourself, and the last section shows you exactly that.

For a guided walkthrough rather than a reference, start with your first agent.

Quick start ​

bash
npm create skein-js@latest my-agent
bash
pnpm create skein-js@latest my-agent
yarn create skein-js@latest my-agent
npx create-skein-js@latest my-agent

Keep the @latest to request the latest published scaffolder instead of relying on a cached version. Node 24 is recommended; generated projects support ^22.12.0 || ^24.0.0 || ^26.0.0.

For an existing workspace, choose a new child directory such as apps/my-agent. Use the Nx generator in Nx workspaces.

What it generates ​

The default storage choice is memory. It keeps development simple and supplies Postgres/Redis services for the default production start. Choose --storage sqlite for a local application; its generated scripts and files differ as described below.

text
my-agent/
├── langgraph.json          Points skein at your graphs; the LangGraph CLI's format
├── package.json            The skein CLI lifecycle as scripts
├── tsconfig.json           Strict, ESM, bundler resolution
├── .nvmrc                  Recommended Node version, or a compatible workspace pin
├── eslint.config.mjs       Recommended rules, unless inherited or tooling is disabled
├── .prettierrc.json         printWidth: 100, unless inherited or tooling is disabled
├── .prettierignore          Generated output, runtime state, dependencies and lockfiles
├── vitest.config.ts
├── compose.dev.yaml        Postgres + Redis — what `start` needs
├── .env                    Ready to use, gitignored — never overwritten if one already exists
├── .env.example            A committed reference copy of it
├── .gitignore
├── README.md               Explains each of these files
└── src/
    ├── echo-graph.ts       Runs with no API key, no network
    ├── echo-graph.test.ts  So `npm test` is green on the first commit
    └── agent-graph.ts      Only with --provider: a ReAct agent with a working tool

The scripts are the whole skein CLI lifecycle:

ScriptCommandWhat it does
devskein dev --port 2024In-memory drivers, hot reload, state persisted to .skein/
dev:servicesdocker compose -f compose.dev.yaml up -d --waitPostgres + Redis, for the durable dev and for start
dev:postgresskein dev … --store postgres --queue redisThe same hot reload, against the drivers production uses
buildskein build --artifact-onlyGraphs → plain JavaScript in .skein/build
startskein start -c .skein/build/langgraph.jsonServe that build — the production entrypoint
typechecktsc --noEmit
testvitest runRun the generated echo graph test
linteslint src --max-warnings=0ESLint, enabled by default
formatprettier --write .Apply formatting, enabled by default
format:checkprettier --check .Check formatting, enabled by default

Those are the default memory scripts. Pick Postgres and dev becomes the Postgres/Redis command, with the in-memory alternative emitted as dev:memory instead of dev:postgres.

dev:services uses --wait so it returns once both healthchecks pass, not merely once the containers exist — otherwise the very next command races first-boot initdb.

start names the artifact's own langgraph.json because skein start serves a build, not a source project: it wants schemas.json beside the config it loads, and that file only exists in .skein/build. Run from the project root like this, it also picks up the POSTGRES_URI and REDIS_URI from the project's .env — skein start reads a conventional .env from its working directory as well as from the config's, because an artifact deliberately carries none of its own (it is the Docker build context). So dev:services && build && start works with nothing to edit first.

Two deliberate choices worth knowing:

  • The default dev needs no credential or service. The echo graph is always present and first in graphs, so the first request works with an empty .env and memory drivers.
  • Memory and Postgres projects include compose.dev.yaml, and the POSTGRES_URI / REDIS_URI in .env are live rather than commented out. The default skein start selects --store postgres --queue redis and requires those services, so a project shipping that start script has to ship both the services and the URIs that reach them, or the script is a trap. The values are the ones the generated compose file serves, so there was never anything to decide.

SQLite projects ​

bash
npm create skein-js@latest my-app -- --storage sqlite
cd my-app
npm run dev
npm run build
npm start

SQLite projects install @skein-js/local, LangGraph, and its SQLite saver. Both dev and start select --store sqlite --queue sqlite, with the database at .skein/state.sqlite; dev:memory keeps the memory alternative. There is no Compose file, service-start script, or Postgres/Redis environment configuration. Graphs still build to .skein/build and the database stays outside it. Use Node 24 (recommended), Node 22.12+ within 22.x, or Node 26. See local deployment for embedding and desktop examples.

Options ​

text
create-skein-js [directory]

  -m, --provider <name>   none | google | anthropic | openai   (default: prompted, else none)
      --storage <name>    memory | postgres | sqlite          (default: prompted, else memory)
      --pm <name>         npm | pnpm | yarn | bun              (default: detected)
      --linting / --no-linting  ESLint and Prettier (default: yes)
      --no-workspace-update Leave workspace registration manual
      --no-install        Skip installing dependencies                (else: prompted, default yes)
      --no-git            Skip initializing a git repository          (else: prompted)
  -y, --yes               Accept every default; never prompt
  -f, --force             Scaffold into a directory that is not empty
  -v, --version
  -h, --help

Passing flags through npm needs a -- separator. pnpm create and npx do not:

bash
npm create skein-js@latest my-agent -- --provider anthropic
pnpm create skein-js@latest my-agent --provider anthropic

--provider ​

Package added.env.example gainsEmits agent-graph.ts?
none (default)——no
google@langchain/google-genaiGOOGLE_API_KEYyes
anthropic@langchain/anthropicANTHROPIC_API_KEYyes
openai@langchain/openaiOPENAI_API_KEYyes

With a provider you also get a ReAct agent wired to a live weather tool that needs no key of its own, so the agent is genuinely runnable the moment you add your model key.

Until the key is set, agent fails to load naming the variable it wants — a load-failure block, while echo keeps serving. skein dev watches .env, so filling the key in takes effect on save.

Behaviour you can rely on ​

  • It never hangs unattended. Prompts appear only when both streams are a TTY, --yes was not passed, and CI is unset. Otherwise it takes the flag, then the default — safe inside a Dockerfile or a CI job.
  • A failed install is not fatal. Your files are already written; the closing output just adds install back to the steps.
  • Scaffolding into a fresh clone works. A directory holding only .git, LICENSE, editor folders or .DS_Store counts as empty, so "create an empty repo, clone it, scaffold into it" needs no --force.
  • git is skipped inside an existing work tree, so it never nests a repository in yours — and the closing output says so, rather than leaving you to infer it from a missing .git. A failure (git absent, or no user.email configured) is reported too, and distinctly: the two used to be the same silent non-event.
  • The version is pinned to a matching runtime. Because every packages/* shares one version, create-skein-js@x.y.z pins skein-js@^x.y.z — the scaffolder and the runtime it scaffolds are always the same release.

Tooling and Node versions ​

ESLint and Prettier default to enabled in both entrypoints. The CLI asks Y/n interactively; --linting and --no-linting bypass that prompt. --yes, CI and non-TTY runs enable tooling. The Nx option linting defaults to true; use --linting=false to opt out.

Projects provide lint, format, and format:check. Missing tooling uses recommended JavaScript and TypeScript ESLint rules and Prettier with printWidth: 100. Existing root ESLint and Prettier configurations are inherited independently. Inherited tooling uses a generated scripts/skein-tools.mjs helper; a standalone project uses the commands shown above. Generated output, dependencies, runtime state and package-manager lockfiles are ignored. Missing tool dependencies are added independently of inherited configuration, and new files follow workspace formatting; see the workspace tooling and caching.

The shared policy recommends Node 24 with engines ^22.12.0 || ^24.0.0 || ^26.0.0. It derives .nvmrc and langgraph.json.node_version from the same selection. Compatible numeric workspace pins in .nvmrc or .node-version are honored. Unsupported numeric pins fail before writing; unresolved aliases such as lts/* produce an explanation and fall back to Node 24. Node 26 is an additional supported runtime; production defaults and desktop packaging remain on Node 24. Yarn users on Node 26 must install Corepack separately because it is no longer bundled with Node.

Nx and other monorepos ​

For Nx 23.2.1 through 23.x, install the generator at the root:

bash
pnpm add -Dw @skein-js/nx
pnpm exec nx g @skein-js/nx:application --directory=apps/my-agent --storage=sqlite
pnpm install
pnpm exec nx dev apps-my-agent

Options are directory (required), provider, storage, and linting. The generator supports integrated workspaces (root dependencies) and package workspaces (application dependencies). It extends the TypeScript base, preserves root lifecycle scripts and leaves installation manual. Use --dry-run to preview without writing. This replaces issue #19's earlier decision to document manual wiring only; existing projects can retain their targets.

Targets use existing nx:run-commands. start depends on build, live targets are uncached, and only .skein/build is a build output. SQLite state and WAL/SHM files stay outside cached inputs and outputs. Retain explicit artifact selection and storage flags, for example skein start -c .skein/build/langgraph.json --store sqlite --queue sqlite. When a diagnostic suggests a config, keep the original command's other options, including custom --sqlite-path settings.

For npm, pnpm, Yarn, Bun, Turborepo or Lerna workspaces:

bash
npm create skein-js@latest apps/my-agent

The CLI retains that directory, detects the workspace and skips installation, including with --yes. Install dependencies from the workspace root after generation. Missing registration is prompted with a yes default; --yes accepts and --no-workspace-update leaves manual instructions. Existing globs, exclusions, YAML comments, key order, versions and unrelated settings are preserved. A pnpm root without pnpm-workspace.yaml gets one containing its existing package.json workspace patterns and exclusions, since pnpm ignores package.json workspaces. Invalid declared workspaces are diagnosed even when another manifest field is invalid. Excluded paths, invalid root metadata, duplicate names and package-manager conflicts fail before writing.

Workspace packageManager metadata takes precedence over lockfile detection. Compatible root or pnpm catalog ranges are reused, then unanimous compatible sibling ranges. Remaining dependencies use tested defaults and report differences. Integrated Nx workspaces reject root dependency conflicts without changing existing versions. For new child directories in Nx workspaces the CLI directs you to the generator before writing.

Workspace tooling and caching ​

Scaffold into a new child directory. The CLI rejects an existing workspace root, including with create-skein-js . --force, before writing files. Nx workspaces use the application generator.

Configuration inheritance and tool dependencies are independent. Existing root ESLint and Prettier configurations stay untouched. A tool declared at the root is reused; otherwise the generated package gets the missing tool dependency (at the root for integrated Nx layouts). Plugins imported by a root configuration remain dependencies of that configuration. Install manually from the workspace root. Both entrypoints format new files with inherited Prettier settings, including singleQuote and semi.

Cached Nx targets include ^default dependency sources. Build, typecheck and test depend on ^build, so imported workspace libraries build first and source edits invalidate the application's cache. Keep the workspace's dependency graph accurate and retain the generated SQLite/WAL/SHM exclusions. start depends on the application's build; live hosts remain uncached.

Verify the generated project ​

Start the generated development command, open its console URL, choose echo, and send hello. Expect echo: hello without model credentials. Then run the generated test command (npm test for an npm project) to check the echo graph without a server. See Testing your project for an SDK smoke test and persistence checks.

Repository validation coverage and publication procedures are contributor topics: scaffold tests and publishing.

If you don't want the scaffolder ​

Nothing here is load-bearing. Two alternatives:

Copy a runnable example. The examples/ directory has one project per framework and pattern:

bash
npx degit skein-js/skein-js/examples/express-basic my-agent

Note that this copies from main, which tracks unreleased work: the examples depend on workspace:* versions that only resolve inside the monorepo, so you will need to replace those with real version ranges. The scaffolder exists partly to avoid exactly that.

Write the three files yourself. A skein project is a graph, a langgraph.json, and the CLI:

bash
npm install -D skein-js
npm install @langchain/core @langchain/langgraph
ts
// src/graph.ts
import { AIMessage } from "@langchain/core/messages";
import { MessagesAnnotation, StateGraph } from "@langchain/langgraph";

export const graph = new StateGraph(MessagesAnnotation)
  .addNode("echo", (state) => ({
    messages: [new AIMessage(`echo: ${state.messages.at(-1)?.content}`)],
  }))
  .addEdge("__start__", "echo")
  .addEdge("echo", "__end__")
  .compile();
json
// langgraph.json
{ "node_version": "24", "graphs": { "agent": "./src/graph.ts:graph" }, "env": ".env" }

Add "type": "module" to your package.json, then npx skein dev. That is the entire contract — see langgraph-cli-compat.md for every field it accepts.

See also ​

The generated JavaScript client dependency follows the runnable examples: currently @langchain/langgraph-sdk ^1.12.7. React frontends use the dedicated @langchain/react package; see the frontend guide for thread subscriptions and interrupt responses.