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. (Lint config lives in the app shell’s biome.json and applies to every member — no biome.json ships in the new repo.) It’s the fastest way to go from idea to “a member of the workspace that typechecks.”

One-shot

npx @tinycld/bootstrap my-feature

The positional argument is the slug - 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 it 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 my-feature \
    --yes \
    --preset full \
    --icon check-square \
    --no-server \
    --no-link

See the bootstrap CLI reference for the full flag list, including the --tooling / --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 app + core if needed, adding your package to the workspaces array, and running npm 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, bring the package into a workspace yourself:

# from a workspace root that already has app/ and core/
cd ~/code/tinycld
npx @tinycld/bootstrap@latest --tooling   # ensures app + core are present
# move/clone your package into a member slot named my-feature, then:
npm install            # links it + runs the generator
cd app && npm run checks

npm run checks (from app/) runs the workspace lint + typecheck with your package present. If it’s green, your scaffolded package is ready to develop in. Run a single member’s checks with npx tinycld-pkg check from the member directory.

Then start the app:

cd ~/code/tinycld/app
npm 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.

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 its own repo (tinycld/core) and a workspace member - the scaffolded tsconfig.json extends ../app/tsconfig.package-base.json and aliases @tinycld/core/* to ../core/*, 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.

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.