> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackra.figentra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scaffolding

> Create a member with pnpm new, what the templates replace, and how to move code with a script instead of by hand.

Never copy an existing member to start a new one. `pnpm new` picks the right directory, package scope, tier tag and script set, copies the target repo's `LICENSE` and formats the result. The shapes it renders are decided in [ADR 0126](/adr/0126-member-shapes-and-scaffold-templates).

```bash theme={"system"}
pnpm new <kind> <name> [--category <c>] [--entries <list>|none] [--description "<text>"] [--title "<Title>"] [--port <n>] [--repo <path>] [--dry-run]
```

Run it with `--dry-run` first to see what it would write. Then run `pnpm install` and `pnpm verify`.

## Kinds

| Kind | Template | Lands in | Pick it for |
| - | - | - | - |
| `service` | `templates/services/service` | `services/<name>` | A backend service: plain TypeScript, root module, no Encore file |
| `package` | `templates/packages/package` + `templates/packages/entries/*` | `packages/<category>/<name>` | Any library, entries-first |
| `tooling` | `templates/packages/tooling` | `packages/tooling/<name>` | A shared config preset, data only |
| `app:console` | `templates/apps/console` | `apps/<name>` | A panel app: Vite, React Router 7, HeroUI; port from 3710 |
| `app:mobile` | `templates/apps/mobile` | `apps/<name>` | iOS and Android: Expo SDK 57, React Navigation, HeroUI Native; no hyphens in the name |
| `app:desktop` | `templates/apps/desktop` | `apps/<name>` | An Electron app packaged by electron-builder |
| `app:docs` | `templates/apps/docs` | `apps/<name>` | A Mintlify site; port from 3334 |

`--category` is required for `package` and must be one of: foundation, application, communication, cross-cutting, data, developer, experience, runtime, security, state. Names are kebab-case. There is no Next.js kind.

## Flags

| Flag | Does |
| - | - |
| `--entries contracts,node,react,native,testing,devtools` | `package` only: the entries besides core (`.`). `none` for core only. Without the flag, a terminal is asked; a script gets core only. |
| `--repo <path>` | Scaffold into another monorepo (for example the private figentra-os). Templates come from this repo; the `LICENSE` comes from the target, or from this repo if the target has none. |
| `--description`, `--title`, `--port` | Fill the tokens; the port defaults to the next free one in the kind's range. |
| `--dry-run` | Print the file list, write nothing. |

Every flag is non-interactive, so agents and CI run `pnpm new` without a prompt.

## Tokens

The scaffold replaces these tokens in file contents **and** path names. Other double-brace text (JSX style objects) is left alone.

| Token | Value |
| - | - |
| `{{NAME}}` | kebab-case short name |
| `{{PASCAL}}` | PascalCase of the name |
| `{{TITLE}}` | Title Case name, or `--title` |
| `{{DESCRIPTION}}` | the `--description` sentence; becomes `description` in `package.json` and opens the README and AGENTS |
| `{{PORT}}` | dev port, the next free one in the kind's range (or `--port`) |
| `{{CATEGORY}}` | the `packages/` folder |
| `{{ENTRIES}}` | the package's tsdown entry map, built from `--entries` |

`TITLE` and `DESCRIPTION` are JSON-escaped when written into `.json` and `.jsonc` files. Template files hold tokens, so oxfmt and oxlint skip `tools/scaffold/templates/**`; the generator formats what it renders.

## What you get

Every member gets `package.json`, `turbo.jsonc` with its one tier tag, `README.md`, `AGENTS.md` with a `## PII` table, `CHANGELOG.md` and the `LICENSE`, with code in `src/` and tests in `__tests__/`. A service and a package also get `config/<name>.config.ts`; a package gets `resources/`. Every template with code ships a passing test, because the vitest preset fails an empty suite. After scaffolding:

1. Replace the placeholder README and AGENTS text with the member's real purpose and its PII table.
2. `pnpm install` to link the new member, then `pnpm verify`.
3. Add a changeset (`pnpm changeset`).

## Changing a template

Edit the template under `tools/scaffold/templates/`, then run `node --test tools/scripts/__tests__/scaffold.test.mjs` (or `pnpm validate`). The test renders every kind into a temporary directory and fails if a token is left in a path or a file, if a rendered `.json` file does not parse, or if the rendered member fails the validator's member, service and exports checks. It also covers package entries, the JSON escaping, the target repo's `LICENSE`, port allocation and `--dry-run`.

When you add a kind, add a row to the test's case list and to `tools/scaffold/README.md`.

## Moves are scripted, never hand edits

Moving or renaming a file or folder means rewriting every import and link that points at it. Do that with a codemod, not an editor: a script `git mv`s each path, rewrites every relative specifier and link from the file's new location, prints what it changed and can run with `--dry-run`.

> [!NOTE]
>
> Status: target. Stackra has no move codemod yet. Port one before the first large restructuring.
> Until then, move in small steps, run `pnpm typecheck` and `pnpm verify` after each, and never mix
> a move with a behaviour change in one commit.

## Checks that back this page

| Rule | Check | State |
| - | - | - |
| Every template renders with no token left | `tools/scripts/__tests__/scaffold.test.mjs` | Live |
| Every rendered member passes the validator's member, service and exports checks | Scaffold test | Live |
| A service declares `stackra.module` and holds no Encore file | `validate-repository.mjs` (`checkService`) | Live |
| Moves are scripted | Move codemod | Not built |

See also [Structure](/guides/structure), [Packages](/guides/packages) and [Definition of done](/guides/definition-of-done).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.