bootstrap CLI

@tinycld/bootstrap does two jobs. In scaffold mode (--new) it produces a new feature package repo that already passes the workspace’s generator checks, CI, and typecheck. In assemble-only mode (--assemble-only) it assembles a workspace — writing the root coordination files from embedded templates and cloning app + core (plus any features you name with --with) into the current directory.

One of the two mode flags is required on every invocation. Running npx @tinycld/bootstrap with no mode flag prints a usage summary and exits 2.

Scaffold mode (--new)

npx @tinycld/bootstrap --new <slug>

The positional argument paired with --new is the package slug — kebab-case, 3–40 characters. It becomes:

Omit the positional to be prompted for it.

Flags

Every prompt has a matching flag. Pass --yes to take defaults for everything else.

FlagTypeDescription
--newbooleanSelects scaffold mode. Mutually exclusive with --assemble-only.
<slug>positionalPackage slug. Required when --yes is set; otherwise prompted.
--yes, -ybooleanSkip all prompts and use defaults. Requires the positional slug.
--namestringHuman-readable name. Defaults to title-cased slug.
--descriptionstringOne-sentence description.
--presetfull | settings-onlyDefaults to full under --yes.
--iconstringLucide icon name. Full preset only. Default box.
--nav-ordernumberInteger 0–99. Full preset only. Default 20.
--shortcutstringSingle lowercase letter, or empty. Full preset only.
--server, --no-serverbooleanInclude the Go server stub. Full preset only. Default true.
--targetstringOutput directory. Default depends on the cwd: if it’s a workspace root, defaults to ./<slug>/; otherwise to ./tinycld-<slug>/<slug>/ (bootstrap mode — see below).
--link, --no-linkbooleanLink the new package into the workspace (assemble/attach + pnpm install at the root). Without either, you’ll be prompted; under --yes, defaults to linking.

--no-link always wins over --yes — pass both when you want to scaffold without touching a workspace.

Non-interactive example

npx @tinycld/bootstrap --new my-feature \
    --yes \
    --preset full \
    --icon check-square \
    --no-server \
    --no-link

This scaffolds with all defaults, no Go server, and no workspace linking. Suitable for CI, scripted setups, and autonomous coding agents.

Assemble-only mode (--assemble-only)

--assemble-only assembles a workspace in the current directory instead of scaffolding a package. It writes the workspace coordination files (package.json, pnpm-workspace.yaml, tinycld.packages.ts, vitest.config.ts, shared test stubs under tests/, .node-version, .go-version) from embedded templates inside bootstrap itself — there is no separate meta-repo to clone. It then clones the members you need: the tinycld shell (which carries @tinycld/core nested inside it) is always cloned, and each --with <feature> adds one feature. Directories that already exist are skipped (and files are never overwritten), so it’s safe to re-run.

# in an empty dir — assembles the workspace (root + app + core + features):
mkdir ~/code/tinycld && cd ~/code/tinycld
npx @tinycld/bootstrap@latest --assemble-only --with mail --with contacts
pnpm install            # links members + runs the generator (postinstall)
FlagTypeDescription
--assemble-onlybooleanSelects assemble-only mode. Mutually exclusive with --new.
--with <feature>string (repeatable)Also clone this feature member. Pass once per feature. Accepts --with name@ref to pin to a tag/branch/commit. Unknown feature names are rejected.

Assemble-only honors TINYCLD_REPO_BASE to pick the git host (CI uses https://github.com/tinycld; the default is git@github.com:tinycld). It does not run pnpm install itself — the caller controls that (so CI can choose pnpm install --frozen-lockfile). A partial checkout is fully supported: npm tolerates members whose directories are absent, and the generator scans only the present ones.

Workspace detection (scaffold mode)

When --target is not set, the scaffolder picks one of two layouts based on the current directory:

This means npx @tinycld/bootstrap --new my-todo from an empty directory produces a self-contained ./tinycld-my-todo/ workspace with everything you need to run the app, while running it from inside a workspace root just adds another member next to app/ and core/.

The detection check is strict: it reads <cwd>/package.json and matches name === "@tinycld/workspace". A coincidentally-named directory won’t false-match.

Prompts (interactive scaffold mode)

PromptExampleNotes
Package slugmy-featureSkipped if passed as argv. Validates kebab-case; minimum 3 chars.
Human-readable nameMy FeatureDefaults to title-cased slug. Used in manifest name and nav label.
One-sentence descriptionDoes a thing well.Used in manifest description, package.json, and README.md.
Presetfull or settings-onlySee Presets.
Lucide iconboxFull preset only. Any lucide-react-native name. Default box.
Nav order20Full preset only. Integer 0–99; controls sidebar position.
Keyboard shortcutfFull preset only. Single lowercase letter, or blank.
Include Go server?y / nFull preset only. If n, server/ and the manifest’s server field are omitted.
Target directory./my-featureMust not exist or must be empty.
Link into workspace?y / nIf yes, the scaffolder assembles (or attaches to) the workspace and runs pnpm install at the root.

Presets

full — data package

The shape of @tinycld/contacts, @tinycld/mail, @tinycld/calendar, @tinycld/drive. You get routes, a sidebar, an optional provider, pbtsdb collections, PocketBase migrations, seed data, and (optionally) a Go server stub. Package TypeScript lives under a tinycld/<slug>/ prefix.

settings-only — service package

The shape of @tinycld/google-takeout-import. The package contributes a single Personal Settings panel — no routes, no nav entry, no collections, no server. Use this for integrations and admin-style tools.

Generated files

Both presets produce:

The full preset additionally produces:

The settings-only preset additionally produces:

After scaffolding

The CLI prints the steps it didn’t run — it never touches git or gh. Typical next steps:

cd my-feature
git init
git add .
git commit -m 'chore: initial scaffold'
gh repo create tinycld/my-feature --public --source=. --push

If you didn’t pass --link (or said no to the prompt), bring the package into a workspace now:

# from a workspace root that already has the tinycld shell
cd ~/code/tinycld
pnpm install            # links the new member + runs the generator
cd tinycld && pnpm run checks

Adding the new package as a present member is enough to see it in navigation; the generator wires routes, collections, migrations, and settings panels automatically.

Import conventions in scaffolded code

Templates use the scoped @tinycld/core path:

import { useOrgLiveQuery } from '@tinycld/core/lib/use-org-live-query'
import { Modal } from '@tinycld/core/ui/modal'

@tinycld/core is a nested member inside the tinycld repo (at tinycld/core/). The scaffolded tsconfig.json extends @tinycld/core/tsconfig.package-base.json by package name, and @tinycld/core/* resolves by package name through the node_modules/@tinycld/* symlink, so resolution works as soon as the package is a present member.

Intra-package imports use relative paths; ~/tinycld/<slug>/* is also aliased to the package’s own nested source if you want an absolute form.

For the published source and template internals, see the bootstrap repo.