TNTStack
Architecture

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@latest
pnpm create @tntstack/app@latest
yarn create @tntstack/app
bunx @tntstack/create-app

The 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
FlagDescription
-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-installSkip pnpm install
--no-gitSkip git init
-b, --branch <branch>Template branch to clone (defaults to master)

Directory Structure

index.ts
scaffold.ts
prompts.ts
consts.ts
clone.ts
index.ts
native.ts
web.ts
version.ts
utils.ts
clean.ts
git.ts
install.ts
package.json
tsconfig.json
tsup.config.ts

How It Works

The scaffold pipeline runs 6 steps in order:

actions/clone.ts
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 from templates/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, and Cargo.toml metadata
  • version.ts — updates version fields across package.json, Cargo.toml, Cargo.lock, tauri.conf.json, and .release-please-manifest.json

The global find-replace covers these identifiers:

Search termReplaced with
com.tntstack.appUser's app identifier
tntstack_lib<project_name_snake>_lib
TNTStackPascalCase project name
odestUser's GitHub username
tntstackUser's project name

Binary files, lock files, and build artifacts are skipped via pattern matching.

Removes files that only belong to the source repo:

consts.ts
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:

PromptDefault
Project name
App identifier (reverse-domain)com.<project_name>.app
GitHub username / orgyour-github-username
Initial version0.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:

ScriptCommand
buildtsup (bundles to dist/index.js)
devtsup --watch
startnode ./dist/index.js
testvitest 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.

On this page