sdxc
SearchCtrlK

[ The sdxc command ]

Start a project

Write a new project from the built-in template or any GitHub repository, then configure the command line with one commented file.

Last updated 2026-10-19

sdxc create writes a new project in the folder layout every other sdxc command expects, and sdxc init writes the one file that changes the command line's defaults.

Create a project

sdxc create my-app
Created my-app from the app template

Next steps:
  cd my-app
  bun install
  bun dev

create writes the built-in app template into my-app: a Remix v3 Worker on Cloudflare, with cloudflare.config.ts, a Vite+ configuration, a test runner and its own command line in bootstrap/cli.ts. It completes the template for the new project:

  • package.json takes the folder's name, lower-cased with any character npm refuses turned into -.

  • Each @sdxc package the template lists is pinned to its latest release, and remix to the version those releases need, so the project starts on one release date.

  • .env.example is copied to .dev.vars, so the development secrets it lists are in place for the first bun dev.

  • .config/sdxc.jsonc is written with every setting spelled out and format set to bunx vp fmt --no-error-on-unmatched-pattern, so the files later commands write match the project's checks.

  • Every TypeScript module credits you in its @author and @copyright tags, from your git user.

create writes files and runs nothing from them: it installs no dependencies and runs no scripts, and prints the commands to run next with the project's package manager.

Where it writes

The target folder must be missing or empty apart from a .git folder, so git init before create works too. A folder holding anything else stops the command, naming what is in the way, and an existing project is never written over. Without a directory, create writes into the current one, and on a terminal it asks where.

When it made the folder, it asks whether to initialize git there, and Enter says yes. --git and --no-git answer in advance; without a terminal the answer is no. A folder that already existed is left as it is, and a missing git is noted and skipped.

What you get

The app template is laid out by kind of code, the layout sdxc make writes into:

bootstrap/        worker.ts, browser.ts, cli.ts: the entry points
app/http/         controllers, middleware, validators
app/initializers/ the router, database, dispatcher, mailer, MCP handler and command line
app/commands/     about, db migrate, db console, deploy, typegen
app/jobs/  app/mcp/  app/models/  app/services/  app/test/
config/           security headers, security.txt, problem types
database/         migrations/
resources/        layouts/, locales/
routes/           web.ts, api.ts

It serves a home page, robots.txt, a sitemap, an API with a health check and an OpenAPI document, and an MCP endpoint. Its package.json has dev, build, start, check, test, typecheck and cli scripts.

Start from your own template

Any GitHub repository is a template as well, with no manifest to declare. Name it as org/repo, with a folder inside it and a branch, tag or commit when you need them:

sdxc create blog --template acme/templates/apps/blog#main --no-git

The repository's files are written as they are, and its package.json, when it has one, takes the project's name; nothing else is changed. GITHUB_TOKEN or GH_TOKEN in the environment authenticates the download, which a private repository needs. When GitHub refuses it and no token is set, a terminal asks for one, used for that download alone.

--template app names the built-in template, and on a terminal create asks which one to use.

Instructions for your agent

The app template also writes AGENTS.md, the instructions AI coding agents read: the commands, where each kind of code goes, and the rules for code built on these packages, between <!-- sdxc:start … --> and <!-- sdxc:end -->. CLAUDE.md imports it for Claude Code. Add your own rules below that section; sdxc upgrade rewrites the section from each new release and keeps the rest.

Your first resource

sdxc create my-app && cd my-app
bun install
sdxc scaffold post title body:text
sdxc run db migrate
bun dev

sdxc scaffold writes the model, migration and pages, sdxc run db migrate applies the migration to the local database, and /posts lists, creates, edits and deletes posts.

Configure the command line

Every setting has a default, so a project needs no configuration file until it changes one. sdxc init writes .config/sdxc.jsonc with every key spelled out, and a $schema your editor completes them from; --dry-run prints it instead:

.config/sdxc.jsonc
{
	"$schema": "https://unpkg.com/@sdxc/kit/config.schema.json",
	"ui": {
		// Where `sdxc ui add` writes components
		"directory": "resources/components/ui",
		// Import prefix for the copies; inferred from tsconfig `paths` when omitted
		// "alias": "~/components/ui",
		// Copy a component's tests beside it
		"tests": false,
	},
	"make": {
		// Route contract `sdxc make controller` adds to, a module in routes/
		"routes": "web",
		// Migration chains by name; `--chain <name>` picks one
		"migrations": { "default": "database/migrations" },
		// Write a test beside every generated file except migrations and factories
		"tests": true,
		// Stubs here replace the built-in ones; `sdxc make stubs` copies them out
		"stubs": ".config/sdxc/stubs",
	},
	"doctor": {
		// Folder of the project's own checks, each module run by `sdxc doctor`
		"checks": ".config/sdxc/checks",
		"migrations": {
			// Migration errors `sdxc doctor` steps past, each a file that already ran and the column its error names
			"accept": [],
		},
	},
	// Command run on every file the CLI writes or edits, paths appended
	// "format": ["bunx", "vp", "fmt", "--no-error-on-unmatched-pattern"],
	// Where the CLI keeps its state; commit it with the project
	"lock": ".config/sdxc.lock.jsonl",
}

The file is JSON with comments, so each setting can say why it is set. format is the one most projects set: a generated file and a copied component then come out in your project's style. It is an argument list run without a shell, once per command, with the written paths appended.

sdxc.jsonc at the project root is read too, when you prefer it there; a project with both files is refused until you delete one. init never overwrites a configuration file that exists.

Every command validates the file before it runs. A misspelt key, a value of the wrong type, or a path outside the project stops the command with one line per problem:

.config/sdxc.jsonc: make.test: Unknown key

Each key is covered with the command it changes: ui in Copy UI components, make in Generate files, doctor in Check a project.

Where to go next

Written by Sergio Xalambrí. Follow @sergiodxa for new packages, or sponsor the work.