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 my-agent
yarn create skein-js my-agent
npx create-skein-js my-agent

Keep the @latest. Without it, npm's npx cache — and pnpm dlx's 24-hour cache — can serve a stale copy of the scaffolder.

What it generates

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
├── vitest.config.ts
├── compose.dev.yaml        Postgres + Redis — what `start` needs
├── .env.example            Every value optional for `dev`
├── .env                    A copy of it, gitignored — never overwritten if one already exists
├── .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 -dPostgres + Redis for start
buildskein build --artifact-onlyGraphs → plain JavaScript in .skein/build
startskein startServe that build — the production entrypoint
typechecktsc --noEmit
testvitest run

Two deliberate choices worth knowing:

  • dev never needs a credential or a service. The echo graph is always present and always first in graphs, so the very first request works with an empty .env.
  • compose.dev.yaml is always generated, not hidden behind a flag. skein start is durable-only — it defaults to --store postgres --queue redis and fails without POSTGRES_URI/REDIS_URI — so a project shipping a start script has to ship the services it needs, or that script is a trap.

Options

text
create-skein-js [directory]

  -m, --provider <name>   none | google | anthropic | openai   (default: prompted, else none)
      --pm <name>         npm | pnpm | yarn | bun              (default: detected)
      --no-install        Skip installing dependencies
      --no-git            Skip initializing a git repository
  -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 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.

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.
  • 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.

Nx and other monorepos

Scaffold into whatever directory you want — the generated project is self-contained, so it works inside a workspace as-is:

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

There is deliberately no skein Nx plugin. A generator collection would be permanent public API tracking Nx's release cadence, and it would buy you one file you can write once and own yourself. Here is that file — apps/my-agent/project.json:

json
{
  "name": "my-agent",
  "projectType": "application",
  "targets": {
    "dev": {
      "executor": "nx:run-commands",
      "cache": false,
      "options": { "command": "skein dev --port 2024", "cwd": "apps/my-agent" }
    },
    "build": {
      "executor": "nx:run-commands",
      "options": { "command": "skein build --artifact-only", "cwd": "apps/my-agent" }
    },
    "start": {
      "executor": "nx:run-commands",
      "cache": false,
      "options": { "command": "skein start", "cwd": "apps/my-agent" }
    },
    "typecheck": {
      "executor": "nx:run-commands",
      "options": { "command": "tsc --noEmit", "cwd": "apps/my-agent" }
    }
  }
}

dev and start are marked "cache": false because a long-running server has no meaningful cached result. Explicit targets work on every Nx version, with no plugin to install and nothing to migrate.

To share the workspace's TypeScript settings, replace the generated tsconfig.json with one that extends your base:

json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": { "noEmit": true, "types": ["node"] },
  "include": ["src/**/*.ts"]
}

The same approach works for Turborepo, pnpm workspaces, or plain npm workspaces — the project is just a package with a langgraph.json in it.

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