Creating a package

@tinycld/bootstrap is the interactive scaffolder for new packages. One command produces a feature repo that matches the conventions every first-party package follows - manifest, CI workflow, tsconfig, sample screens or a settings panel, and (optionally) a Go server stub. (The canonical lint config lives at tinycld/biome.json and applies to every member — no biome.json ships in the new repo by default.) It’s the fastest way to go from idea to “a member of the workspace that typechecks.”

One-shot

npx @tinycld/bootstrap --new my-feature

--new <slug> selects scaffold mode (the alternative is --assemble-only, which assembles a workspace instead of scaffolding a package — see the CLI reference). The slug is kebab-case, 3–40 chars; it becomes the npm package name (@tinycld/my-feature), the URL segment (/a/[orgSlug]/my-feature/), and the Go module path (tinycld.org/packages/my-feature). Omit the slug to be asked.

Where the scaffolder puts things

The default target directory depends on whether you’re already in a TinyCld workspace:

The detection looks for a package.json whose name is @tinycld/workspace — a coincidentally-named directory won’t false-match.

You can always override the auto-detection with --target ./somewhere-else.

Prompts

The scaffolder walks you through a short interactive session:

Every prompt has a matching flag, so the scaffolder can run fully non-interactively (handy for CI, scripted setups, and autonomous coding agents):

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

See the bootstrap CLI reference for the full flag list, including the --assemble-only / --with workspace-assembly mode.

Presets

full - data package

Matches @tinycld/contacts, @tinycld/mail, @tinycld/calendar, @tinycld/drive. Package TypeScript lives under a tinycld/<slug>/ prefix (which the package.json exports map maps to subpaths like @tinycld/my-feature/screens/*). You get:

settings-only - service package

Matches @tinycld/google-takeout-import. No routes, no nav entry, no collections, no server - just a settings panel:

Use this for integrations (import/export tools), admin surfaces, or anything that lives entirely under /a/<orgSlug>/settings/.

After scaffolding

If you accepted the Link into the workspace now? prompt, the scaffolder has already assembled (or attached to) the workspace - writing the workspace package.json, cloning the tinycld shell (with @tinycld/core nested) if needed, and running pnpm install at the root. Only git and GitHub remain manual:

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

If you declined the link prompt, or want to link later, run @tinycld/bootstrap in assemble-only mode from the workspace root to make sure the tinycld shell is present, then install:

# from a workspace root (or an empty dir you want to become one)
cd ~/code/tinycld
npx @tinycld/bootstrap@latest --assemble-only   # ensures the tinycld shell is present
# place your package directory (named after its slug) next to tinycld/, then:
pnpm install            # links it + runs the generator
cd tinycld && pnpm run checks

pnpm run checks (from tinycld/) runs the ecosystem-wide Biome pass and the app’s typecheck. If it’s green, your scaffolded package is ready to develop in. Run a single member’s typecheck + unit tests with pnpm exec tinycld-pkg check from the member directory.

Then start the app:

cd ~/code/tinycld/tinycld
pnpm run dev

This builds and runs the Go PocketBase server, the Expo dev server, and a single-port HTTP proxy that fronts both. Open the URL it prints (default http://localhost:7100, or https:// if a localhost cert is present). For the full preset, your package’s nav entry shows up in the sidebar; for settings-only, your panel appears under the org settings.

To log in, first run pnpm run db:reset (from tinycld/) once — it seeds a test user and org and prints the credentials to use. See Logging in for the details.

What the templates assume

Scaffolded code imports core via the scoped path:

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

Intra-package imports use relative paths. ~/tinycld/<slug>/* is also aliased to the package’s own nested source.

@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. Don’t install core as a dependency. See Screens for the full story.

Contributing to another package’s sidebar

Not every new package wants its own nav entry. If your feature naturally extends one that already exists — for example, booking pages that belong inside the calendar sidebar — you can scaffold a package without nav or routes and ship a sidebar contribution instead. Drop sidebarContributions into your manifest, point at a component file, and add the matching package.json exports wildcard. The full pattern (including ordering, validation, and runtime gating with usePackages()) is documented in Sidebar slots.

For what each generated file means, walk through Anatomy. For bringing the new package into a workspace once it’s pushed, see Adding a package.