docs(apps): fix stale CLI commands in getting-started and operations pages (#22688)

Part 1 of a series of small PRs from a full audit of the app-development
docs (every claim was cross-checked against `twenty-sdk`,
`create-twenty-app`, and a scaffolded app).

## What this fixes

**quick-start.mdx**
- `yarn twenty server` does not exist — replaced with `yarn twenty
docker:start` (the command every other page uses, and what the CLI
actually ships).
- The scaffolder is non-interactive since create-twenty-app 2.x: there
is no "name and description" prompt and no "Would you like to set up a
local Twenty instance?" prompt (that screenshot was removed). It
auto-starts the local Docker server and authenticates with the
pre-seeded dev key; OAuth (browser sign-in + Authorize) only happens for
remote `--url` targets or `--authentication-method oauth`.
- `--debounceMs` default is `1000`, not `2000` (see `twenty dev
--help`).
- The one-shot section now teaches `twenty plan` / `twenty apply`; `dev
--once` / `--dry-run` are marked as the deprecated aliases they are in
the CLI help.
- Node prerequisite tightened to 24.5+ to match `engines.node: ^24.5.0`.

**operations/sync-and-recovery.mdx**
- Command matrix, previewing section, and recovery ladder switched from
the deprecated `dev --once [--dry-run]` to `plan` / `apply` (heading
anchor updated accordingly).

**operations/cli.mdx**
- Added a complete command overview table (the page previously omitted
`plan`, `apply`, `dev:translations-extract`, `dev:catalog-sync`, and the
whole `docker:*` group without pointing anywhere).
- Added `remote:status` and `remote:remove`, and the `--preInstall` exec
flag.

**tutorials/document-generator/publishing.mdx**
- Pre-publish check now uses `yarn twenty plan`.

Only English sources were touched; `l/<locale>` copies come from
Crowdin.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01ExboyDAT19khDuKXaYXETT)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22688?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Martin <martin@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
martmull
2026-07-09 10:25:09 +02:00
committed by GitHub
parent cdabef6429
commit 5c1e7dd559
4 changed files with 81 additions and 52 deletions
@@ -6,7 +6,7 @@ description: Create your first Twenty app in minutes.
## Prerequisites
- **Node.js 24+** — [Download](https://nodejs.org/)
- **Node.js 24.5+** — [Download](https://nodejs.org/)
- **Yarn 4** — bundled with Node via Corepack. Enable it: `corepack enable`
- **Docker** — [Download](https://www.docker.com/products/docker-desktop/). Needed to run a local Twenty server. Skip if you already have Twenty running elsewhere.
@@ -15,7 +15,7 @@ Building a Twenty app has three phases. The scaffolder collapses them into one h
| Phase | What you do | Tool | Result |
|---|---|---|---|
| **1. Scaffold** | Generate the app's source code | `npx create-twenty-app` | A TypeScript project on disk |
| **2. Run a server** | Start a Twenty server to sync into | Docker + `yarn twenty server` | A running Twenty instance |
| **2. Run a server** | Start a Twenty server to sync into | Docker + `yarn twenty docker:start` | A running Twenty instance |
| **3. Sync** | Live-sync your code to the server | `yarn twenty dev` | Your changes appear in the UI |
---
@@ -28,7 +28,7 @@ Create a new app from the template:
npx create-twenty-app@latest my-twenty-app
```
You'll be prompted for a name and description — press **Enter** for the defaults. This generates a TypeScript project in `my-twenty-app/` with a starter `application-config.ts`, a default role, a CI workflow, and an integration test.
The scaffolder is non-interactive: the directory name becomes the app name. Pass `--display-name` and `--description` to customize the generated metadata (you can also edit it later in `src/constants/universal-identifiers.ts`). This generates a TypeScript project in `my-twenty-app/` with a starter `application-config.ts`, a default role, CI/CD workflows, and an integration test.
**After this phase:** you have an app's source code on your machine. It isn't running yet — that's Phase 2.
@@ -38,28 +38,14 @@ You'll be prompted for a name and description — press **Enter** for the defaul
Your app needs a Twenty server to sync into. The server is a full Twenty instance — UI, GraphQL API, PostgreSQL — running locally in Docker. Your local code uploads its definitions to that server, which makes them appear in the UI.
The scaffolder offers to start one for you:
The scaffolder starts one for you: with Docker running, it pulls the `twentycrm/twenty-app-dev` image, starts it on port `2020`, and authenticates the CLI against the pre-seeded demo workspace (`tim@apple.dev`) — no sign-in required.
> **Would you like to set up a local Twenty instance?**
- **Yes (recommended)** — pulls the `twentycrm/twenty-app-dev` Docker image and starts it on port `2020`. Make sure Docker is running first.
- **No** — choose this if you already have a Twenty server you want to connect to. You can wire it up later with `yarn twenty remote:add`.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/start-instance.png" alt="Should start local instance?" />
</div>
Once the server is up, a browser opens for sign-in. Use the pre-seeded demo account:
- **Email:** `tim@apple.dev`
- **Password:** `tim@apple.dev`
To connect to an existing Twenty server instead, pass `--url <your-server-url>`. Remote servers authenticate with OAuth: a browser opens so you can sign in and click **Authorize**, which gives the CLI access to your workspace. (You can also opt into OAuth locally with `--authentication-method oauth` — sign in with `tim@apple.dev` / `tim@apple.dev`.)
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/login.png" alt="Twenty login screen" />
</div>
Click **Authorize** on the next screen — this gives the CLI access to your workspace.
<div style={{textAlign: 'center'}}>
<img src="/images/docs/developers/extends/apps/authorize.png" alt="Twenty CLI authorization screen" />
</div>
@@ -117,27 +103,31 @@ Click **View installed app** to see the workspace install. The **About** tab sho
### One-shot sync for CI and scripts
Pass `--once` to run a single build + sync and exit — same pipeline, no watcher:
Use `plan` and `apply` to run the same pipeline once, without a watcher:
```bash filename="Terminal"
yarn twenty dev --once
yarn twenty plan # preview the metadata changes without applying them
yarn twenty apply # show the plan, then apply it
```
| Command | Behavior | When to use |
|---------|----------|-------------|
| `yarn twenty dev` | Watches and re-syncs on every change. Runs until you stop it. | Interactive local development. |
| `yarn twenty dev --once` | Single build + sync, exits `0` on success, `1` on failure. | CI, pre-commit hooks, AI agents, scripted workflows. |
| `yarn twenty dev --once --dry-run` | Builds and prints the metadata changes **without applying them**. | Inspecting what a sync would change before committing to it. |
| `yarn twenty apply` | Single build + sync, exits `0` on success, `1` on failure. Asks for confirmation on destructive changes (pass `--force` to skip). | CI, pre-commit hooks, AI agents, scripted workflows. |
| `yarn twenty plan` | Builds and prints the metadata changes **without applying them**. | Inspecting what a sync would change before committing to it. |
Both modes need an authenticated remote. See [Syncing & recovery](/developers/extend/apps/operations/sync-and-recovery#previewing-changes-dry-run) for more on `--dry-run`.
All modes need an authenticated remote. See [Syncing & recovery](/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) for more on `plan`.
<Note>
`yarn twenty dev --once` and `yarn twenty dev --once --dry-run` are deprecated aliases for `yarn twenty apply` and `yarn twenty plan`.
</Note>
### Dev mode options
| Flag | Description |
|------|-------------|
| `--once` | Build and sync once, then exit. |
| `--dry-run` | With `--once`, preview the metadata changes without applying them. Writes nothing. |
| `--debounceMs <ms>` | Set the file-change debounce delay in milliseconds (default: `2000`). |
| `--force` | Apply destructive changes (deletes) without confirmation. |
| `--debounceMs <ms>` | Set the file-change debounce delay in milliseconds (default: `1000`). |
| `--verbose` / `--debug` | Show detailed build logs, sync requests, and error traces. |
## What you can build
@@ -4,7 +4,25 @@ description: yarn twenty commands for executing functions, streaming logs, manag
icon: "terminal"
---
Beyond `dev`, `dev:build`, `dev:add`, and `dev:typecheck`, the `yarn twenty` CLI provides commands for executing functions, viewing logs, and managing app installations.
The `yarn twenty` CLI is your interface to everything app-related. Full command list:
| Command | What it does | Documented in |
|---------|--------------|---------------|
| `dev` | Watch source files and live-sync changes | [Quick Start](/developers/extend/apps/getting-started/quick-start) |
| `plan` | Preview metadata changes without applying them | [Syncing & recovery](/developers/extend/apps/operations/sync-and-recovery#previewing-changes-plan) |
| `apply` | Apply metadata changes after showing the plan | [Syncing & recovery](/developers/extend/apps/operations/sync-and-recovery) |
| `dev:build` | Compile the app and generate the API client (`--tarball` to pack a `.tgz`) | [Publishing](/developers/extend/apps/operations/publishing) |
| `dev:typecheck` | Run TypeScript type checking | [Testing](/developers/extend/apps/operations/testing) |
| `dev:add` | Scaffold a new entity | [Scaffolding](/developers/extend/apps/getting-started/scaffolding) |
| `dev:generate-client` | Regenerate the typed API client | this page |
| `dev:function:exec` / `dev:function:logs` | Execute functions and stream their logs | this page |
| `dev:translations-extract` | Extract translatable strings into `locales/` catalogs | [Translations](/developers/extend/apps/translations/overview) |
| `dev:catalog-sync` | Trigger a marketplace catalog sync | [Publishing](/developers/extend/apps/operations/publishing#how-marketplace-discovery-works) |
| `app:publish` / `app:install` / `app:uninstall` | Release lifecycle | [Publishing](/developers/extend/apps/operations/publishing) and this page |
| `docker:*` | Manage the local Twenty server container | [Local Server](/developers/extend/apps/getting-started/local-server) |
| `remote:*` | Manage server connections | this page |
Every command accepts `-r, --remote <name>` to target a specific remote instead of the default one.
## Executing functions (`yarn twenty dev:function:exec`)
@@ -20,8 +38,9 @@ yarn twenty dev:function:exec -u e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf
# Pass a JSON payload
yarn twenty dev:function:exec -n create-new-post-card -p '{"name": "Hello"}'
# Execute the post-install function
# Execute the install hooks
yarn twenty dev:function:exec --postInstall
yarn twenty dev:function:exec --preInstall
```
## Viewing function logs (`yarn twenty dev:function:logs`)
@@ -100,6 +119,12 @@ yarn twenty remote:list
# Set the active remote
yarn twenty remote:use <name>
# Check that the active remote's authentication is still valid
yarn twenty remote:status
# Remove a remote
yarn twenty remote:remove <name>
```
Your credentials are stored in `~/.twenty/config.json`.
@@ -15,33 +15,44 @@ For day-to-day local iteration you almost always want `yarn twenty dev`. Deployi
| You want to… | Command | Notes |
| --- | --- | --- |
| Iterate locally with live sync | `yarn twenty dev` | Watches your files and syncs on every change. |
| Sync once and exit (CI, scripts, hooks) | `yarn twenty dev --once` | One build + sync, then exits. |
| Preview changes **without applying them** | `yarn twenty dev --once --dry-run` | Computes and prints the diff; writes nothing. |
| Sync once and exit (CI, scripts, hooks) | `yarn twenty apply` | One build + sync, then exits. Add `--force` to skip the destructive-change confirmation. |
| Preview changes **without applying them** | `yarn twenty plan` | Computes and prints the diff; writes nothing. |
| Remove the app from the workspace | `yarn twenty app:uninstall` | Add `--yes` to skip the prompt. |
| Ship a tarball to a server | `yarn twenty app:publish --private` | Requires a **strictly higher** `package.json` version — see [Publishing](/developers/extend/apps/operations/publishing). |
| Publish to the marketplace (npm) | `yarn twenty app:publish` | — |
| Install / upgrade a deployed version | `yarn twenty app:install` | Installs the version currently deployed. |
| Wipe the local server and start clean | `yarn twenty docker:reset` | Deletes **all** local data — last resort. |
<Note>
`yarn twenty dev --once` and `yarn twenty dev --once --dry-run` still work as deprecated aliases for `yarn twenty apply` and `yarn twenty plan`.
</Note>
### Local sync does not need a version bump
The strictly-increasing `version` rule (`VERSION_ALREADY_EXISTS` on deploy, `APP_ALREADY_INSTALLED` / `CANNOT_DOWNGRADE_APPLICATION` on install) applies to **`app:publish` / `app:install`** — the release path. `yarn twenty dev` syncs your manifest in place and never requires a version change, so you don't need to touch `package.json` to iterate. If you find yourself bumping the version to test a local change, you're using the release path when you want the dev loop.
## Reading the sync output
Every sync prints the metadata changes it applied (or would apply, with `--dry-run`):
Every sync prints the metadata changes it applied (or would apply, with `plan`), Terraform-style — one block per entity with its attributes, then a summary line:
```text filename="Terminal"
Metadata changes: 2 created, 1 updated, 1 deleted
created objectMetadata rocket
created fieldMetadata timelineActivities
updated fieldMetadata launchedAt
deleted pageLayout legacyTab
✓ Synced
# objectMetadata "rocket" will be created
+ icon = "IconRocket"
+ labelSingular = "Rocket"
+ ...
# fieldMetadata "launchedAt" will be updated
~ isNullable = false -> true
Plan: 2 to add, 1 to change, 1 to destroy.
✓ Synced My App (4 files)
```
This is your first diagnostic: it tells you exactly which objects, fields, and layouts changed, so you can confirm a sync did what you expected before checking the UI.
Destructive changes (`to destroy`) are listed with what they drop (e.g. `objectMetadata "auditNote" — drops the table and all its rows`) and require interactive confirmation, or `--force` in scripts.
When a sync fails on a single entity, the error names the offending entity and its `universalIdentifier`, for example:
```text
@@ -50,39 +61,42 @@ Migration action 'create' for 'fieldMetadata' (universalIdentifier: 2020...4337)
Use that identifier to find the entity in your manifest (and, if needed, in the workspace) instead of guessing which one conflicts.
## Previewing changes (dry run)
## Previewing changes (plan)
`yarn twenty dev --once --dry-run` builds your manifest, asks the server for the migration plan, and prints it — **without applying anything**. It's the safe way to answer "what would this sync change?" before committing to it.
`yarn twenty plan` builds your manifest, asks the server for the migration plan, and prints it — **without applying anything**. It's the safe way to answer "what would this sync change?" before committing to it.
```bash filename="Terminal"
yarn twenty dev --once --dry-run
yarn twenty plan
```
```text filename="Terminal"
Building manifest...
Computing metadata diff (dry run, nothing will be applied)...
Metadata changes: 1 created, 1 updated
created fieldMetadata timelineActivities
updated objectMetadata rocket
✓ Dry run complete for My App — no changes were applied
Computing metadata plan (read-only, nothing will be applied)...
# fieldMetadata "timelineActivities" will be created
+ ...
Plan: 1 to add, 1 to change, 0 to destroy.
✓ Plan complete for My App — no changes were applied
```
A dry run:
A plan:
- **Writes nothing** — no metadata migration, no application record update, no default role/tab changes, and no API client generation.
- Returns the **same diff** a real sync would apply, so you can review created/updated/deleted entities up front.
- Is useful before a risky change, when reviewing an AI-generated change, or in a script that should fail if an unexpected change is about to land.
<Note>
A dry run only previews **metadata** changes, and it requires the app to have been synced at least once (so the workspace knows about it). If you run it against an app that was never synced, the server reports that the app is not installed — run `yarn twenty dev` once first.
A plan only previews **metadata** changes, and it requires the app to have been synced at least once (so the workspace knows about it). If you run it against an app that was never synced, the server reports that the app is not installed — run `yarn twenty dev` once first.
</Note>
## Recovery ladder
When local metadata looks wrong, escalate in this order and stop as soon as you're unblocked. Each step is more disruptive than the last.
1. **Re-sync.** Run `yarn twenty dev --once` again. Syncs are idempotent — re-running a clean manifest is safe and often resolves a transient hiccup.
2. **Preview the plan.** Run `yarn twenty dev --once --dry-run` to see exactly what the next sync intends to change, without applying it.
1. **Re-sync.** Run `yarn twenty apply` again. Syncs are idempotent — re-running a clean manifest is safe and often resolves a transient hiccup.
2. **Preview the plan.** Run `yarn twenty plan` to see exactly what the next sync intends to change, without applying it.
3. **Read the named error.** If a sync fails, note the metadata type and `universalIdentifier` in the message (see above) and locate that entity in your manifest. A conflict usually points to a duplicated or re-used identifier.
4. **Uninstall and reinstall.** `yarn twenty app:uninstall`, then sync again (`yarn twenty dev`). This rebuilds the app's metadata from a clean slate while keeping the rest of your workspace intact.
5. **Full reset (last resort).** `yarn twenty docker:reset`, then re-seed and re-sync.
@@ -77,10 +77,10 @@ Run the same gates CI does:
yarn lint # oxlint
yarn typecheck # tsgo
yarn test:unit # unit tests
yarn twenty dev --once --dry-run # preview the metadata diff
yarn twenty plan # preview the metadata diff
```
The dry run prints exactly what would change on the server without applying it —
The plan prints exactly what would change on the server without applying it —
a good final sanity check. See
[Testing](/developers/extend/apps/operations/testing) and
[Syncing & recovery](/developers/extend/apps/operations/sync-and-recovery).