CLI Package
Scaffolding CLI. Downloads, renames, and configures TNTStack projects.
Published to npm as @tntstack/create-app. Downloads the latest source, renames all identifiers to match your project, configures Android and iOS packaging, inits git, and optionally installs dependencies.
Usage
npm create @tntstack/app@latestpnpm create @tntstack/app@latestyarn create @tntstack/appbunx @tntstack/create-appThe CLI runs in interactive mode by default. You can also pass flags for CI or scripted setups:
npm create @tntstack/app@latest --name <your-project-name> --github-user <your-github-username> --no-install --no-git| Flag | Description |
|---|---|
-n, --name <name> | Project name |
-d, --directory <dir> | Output directory (defaults to ./<name>) |
-i, --identifier <id> | App identifier, reverse-domain (defaults to com.<name>.app) |
-g, --github-user <user> | GitHub username or org |
-v, --app-version <ver> | Initial version (defaults to 0.1.0) |
--no-install | Skip pnpm install |
--no-git | Skip git init |
-b, --branch <branch> | Template branch to clone (defaults to master) |
Directory Structure
How It Works
The scaffold pipeline runs 6 steps in order:
import { downloadTemplate } from "giget";
import { TEMPLATE_SOURCE } from "../consts.js";
export async function cloneTemplate(
directory: string,
branch: string = "master",
): Promise<string> {
const { dir } = await downloadTemplate(`${TEMPLATE_SOURCE}#${branch}`, {
dir: directory,
force: true,
});
return dir;
}Downloads the source from github:odest/tntstack using giget. The branch defaults to master but can be overridden with --branch. No git history is carried over.
Replaces branded content with clean starter files (shadow templates):
apps/web/content/docs/→ replaced with minimal starter docs fromtemplates/docs/- Root
README.md→ replaced with a project template,{{PROJECT_NAME}}is substituted with the user's project name
This ensures the scaffolded project starts with a clean documentation structure instead of the TNTStack source docs.
The most complex step. Split into focused modules under actions/rename/:
utils.ts— shared helpers (shouldSkip,replaceInFile)web.ts— renames web-specific config (site.ts,layout.tsx,next.config.mjs)native.ts— renames Tauri config, Android/iOS identifiers, andCargo.tomlmetadataversion.ts— updates version fields acrosspackage.json,Cargo.toml,Cargo.lock,tauri.conf.json, and.release-please-manifest.json
The global find-replace covers these identifiers:
| Search term | Replaced with |
|---|---|
com.tntstack.app | User's app identifier |
tntstack_lib | <project_name_snake>_lib |
TNTStack | PascalCase project name |
odest | User's GitHub username |
tntstack | User's project name |
Binary files, lock files, and build artifacts are skipped via pattern matching.
Removes files that only belong to the source repo:
export const FILES_TO_CLEAN = [
"CHANGELOG.md",
"CONTRIBUTING.md",
"packages/cli",
".github/FUNDING.yml",
".github/workflows/publish-cli.yml",
".github/assets",
] as const;Also removes packages/cli references from release-please-config.json and .release-please-manifest.json.
Runs git init and creates an initial commit. Skipped if --no-git was passed or if the user chose "No" in the interactive prompt. Falls back gracefully if git is not available on the system.
Runs pnpm install unless --no-install was passed. Before attempting installation, the CLI checks if pnpm is available on the system. If pnpm is not found, the project is still created successfully but dependencies are skipped with a helpful message guiding the user to install pnpm and run pnpm install manually.
Interactive Prompts
In interactive mode, the CLI asks these questions:
| Prompt | Default |
|---|---|
| Project name | — |
| App identifier (reverse-domain) | com.<project_name>.app |
| GitHub username / org | your-github-username |
| Initial version | 0.1.0 |
| Install dependencies? | Yes |
| Initialize a new git repository? | Yes |
After answering, a summary is shown and the user can confirm or re-enter their choices.
Build & Development
The CLI is built with tsup and published independently from the monorepo:
| Script | Command |
|---|---|
build | tsup (bundles to dist/index.js) |
dev | tsup --watch |
start | node ./dist/index.js |
test | vitest run |
The CLI version is auto-injected from package.json at build time via tsup.config.ts define, so it always stays in sync.
The CLI has its own version (vX.Y.Z) separate from the monorepo version. It's published to npm via the publish-cli.yml GitHub Actions workflow on new releases.